Toggle navigation
在线编辑器
在线代码
文本比较
jQuery下载
前端库
在线手册
登录/注册
下载代码
html
css
js
分享到微信朋友圈
X
html
css
body { background-color: #000; }
JavaScript
"use strict"; window.addEventListener("load",function() { /* variante ou le Nicolas se déplace en suivant l'orientation de son segment central, et en réapparaissant d'un bord sur l'autre */ let canv, ctx; let maxx, maxy; let arretAnim,arretAnimSync,suiteAnim; let lRef ; // min de maxx, maxy, sert de référence pour mise à l'échelle du dessin let nbNic; // nombre de Nicolas à animer let tbNic; // table des Nicolas // pour éviter de toujours écrire Math. const mrandom = Math.random; const mfloor = Math.floor; const mround = Math.round; const mabs = Math.abs; const mmin = Math.min; const mmax = Math.max; const mPI = Math.PI; const mPIS2 = Math.PI / 2; const m2PI = Math.PI * 2; const msin = Math.sin; const mcos = Math.cos; const matan2 = Math.atan2; const mhypot = Math.hypot; const msqrt = Math.sqrt; // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - function Redemarrer() { maxx=window.innerWidth-16; maxy=window.innerHeight-50; canv.width=maxx; canv.height=maxy; ctx.clearRect(0,0,maxx,maxy); lRef = mmin(maxx,maxy); arretAnim,arretAnimSync =false; // ordre marche / arrêt nbNic = 20; tbNic=[]; for (let kn=0; kn < nbNic; kn++) { tbNic[kn] = new Nicolas(); tbNic[kn].dessiner(); } // for kn enchaine(); // lance animation } // Redemarrer // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - function calculerArc (p0,p1,p2,p3) { // calcule une courbe de Bézier entre p1 et p2 // la tangente en p1 est parallèle à p0-p2 // la tangente en p2 est parallèle à p1-p3 // rend un tableau contenant les 2 points de contr?le et le point d'arrivée let dx, dy,longueur,ltg,lctrl; let x1c,y1c; // coordonnées 1er point de contr?le let x2c,y2c; // coordonnées 2e point de contr?le const coeffDir=0.3; // coefficient pour calcul des points de contr?le longueur= mhypot(p2[0]-p1[0], p2[1]-p1[1]); lctrl=longueur*coeffDir // calcul du 1er point de contr?le dx=p2[0]-p0[0]; dy=p2[1]-p0[1]; ltg=mhypot(dx,dy); x1c= p1[0] + lctrl * dx/ltg; y1c= p1[1] + lctrl * dy/ltg; // calcul du 2e point de contr?le dx=p1[0]-p3[0]; dy=p1[1]-p3[1]; ltg=mhypot(dx,dy); x2c= p2[0] + lctrl * dx/ltg; y2c= p2[1] + lctrl * dy/ltg; return [[x1c ,y1c], [x2c, y2c], p2]; } // function dessinerArc // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - function alea (mini,maxi,entier) { // entrée : mini et maxi nombres, avec maxi > mini (non vérifié) // rend un nombre théoriquement entre mini inclus et maxi exclus // si 'entier' absent ou == false : // rend un flottant // si 'entier' == true // mini et maxi doivent être des entiers // la valeur rendue est entière let x=mini + mrandom() * (maxi-mini); if (entier) return mfloor(x); return x; } function salea() { // signe aléatoire : rend -1 ou 1, au hasard return (mrandom()< 0.5) ? -1 : 1; } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // classe nicolas function Nicolas() { /* squelette */ const nbSegmentsMini = 2; // 2 segments au moins const nbSegmentsMaxi = 5; // 5 segments au plus this.nbSegments = alea( nbSegmentsMini, nbSegmentsMaxi+1, 1); // mini au maxi inclus this.nbSegments=2; /* better for paramecia */ this.pt0 = [alea(0,maxx),alea(0,maxy)]; // coordonnées du point de base this.pt0 = [maxx / 2, maxy / 2]; this.coeffsD=[]; // coeffs des 'points à droite' et 'points à gauche' qui déterminent this.coeffsG=[]; // l'épaisseur du blob sur le squelette const lVoulue = alea(0.2, 0.7) * lRef; this.longueurs=[]; // on détermine la longueur de chaque segment pour que le total soit <= lVoulue this.lSegMaxi = lVoulue / this.nbSegments; this.lSegMini = 0.5 * this.lSegMaxi; this.ang0 = alea(0,m2PI); // this.dAngles = []; // changements d'angle entre segments this.dAngMaxi = 0.1 * mPI; this.dAngMini = -this.dAngMaxi; for (let ks = 0 ; ks < this.nbSegments; ++ks) { this.longueurs.push (alea(this.lSegMini, this.lSegMaxi)); // direction du prochain segment (si pas dernier segment) if (ks < this.nbSegments-1) { this.dAngles[ks] = alea( this.dAngMini, this.dAngMaxi); } // si pas dernier point } // for kp this.dPt0 = this.dPt0Alea(); // fin du squelette // on va déterminer les coeffs qui définissent l'épaisseur du blob for (let ks=0; ks < this.nbSegments-1; ++ks) { this.coeffsG[ks]= alea(0.1,0.2); this.coeffsD[ks]= alea(0.1,0.2); } // calcul des points du blob this.nBlob = calculBlob( this.pt0, this.ang0, this.nbSegments, this.longueurs, this.dAngles, this.coeffsD, this.coeffsG, this); this.couleur = alea(0,360); this.dCouleur = this.dCouleurAlea(); // pour l'animation this.dAng0 = this.dAng0Alea(); this.ddAngles = []; this.dlongueurs = []; for (let kp=0; kp < this.nbSegments; ++kp) { this.ddAngles.push (this.ddAngAlea() * salea()); // un de trop, pas grave this.dlongueurs.push (this.dlAlea() * salea()); } } // function Nicolas() // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Nicolas.prototype.dPt0Alea = function () { // résultat positif return alea(0.002,0.003) * lRef; } // dPtAlea Nicolas.prototype.dlAlea = function() { // rend une variation de longueur aléatoire positive return alea(1/100,1/50) * (this.lSegMaxi-this.lSegMini); } // Nicolas.prototype.dlAlea Nicolas.prototype.ddAngAlea = function() { // rend une variation de variation d'angle aléatoire positive return alea(1/200,1/100) * (this.dAngMaxi-this.dAngMini); } // Nicolas.prototype.ddAngAlea Nicolas.prototype.dAng0Alea = function() { // rend une variation d'angle aléatoire (angle du milieu) positive ou négative return alea(1/500,1/200) * salea(); } // Nicolas.prototype.dAng0Alea Nicolas.prototype.dCouleurAlea = function() { // rend une variation de couleur positive ou négative return alea(0.2,0.5) * salea(); } // Nicolas.prototype.dAngAlea // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Nicolas.prototype.bouger = function() { this.ang0 += this.dAng0; // rotation de la base if (this.ang0 > m2PI) this.ang0 -= m2PI; else if (this.ang0 < 0) this.ang0 += m2PI; if (mrandom() < 0.01 ) this.dAng0 = this.dAng0Alea(); this.couleur += this.dCouleur; // variation de couleur if (alea(0,100) < 1) this.dCouleur = this.dCouleurAlea(); // déplacement du point central // a lieu dans la direction du segment de tête let [x0,y0]=this.pt0; x0 += this.dPt0 * mcos(this.direction); y0 += this.dPt0 * msin(this.direction); if (x0 > maxx) x0 -= maxx; if (x0 < 0 ) x0 += maxx; if (y0 > maxy) y0 -= maxy; if (y0 < 0 ) y0 += maxy; this.pt0 = [x0, y0]; // on applique les variations de longueur des segments let longueurs = this.longueurs; let dlongueurs = this.dlongueurs; let lSegMini = this.lSegMini; let lSegMaxi = this.lSegMaxi; for (let ks=0; ks
lSegMaxi) { longueurs[ks] = lSegMaxi; dlongueurs[ks] = - this.dlAlea(); } else if (this.longueurs[ks] < lSegMini) { longueurs[ks] = lSegMini; dlongueurs[ks] = this.dlAlea(); } } // for ks // on applique les variations d'angles let dAngles = this.dAngles; let ddAngles = this.ddAngles; let dAngMini = this.dAngMini; let dAngMaxi = this.dAngMaxi; for (let ks=0; ks
dAngMaxi) { dAngles[ks] = dAngMaxi; ddAngles[ks] = -this.ddAngAlea(); } else if (dAngles[ks] < dAngMini) { dAngles[ks] = dAngMini; ddAngles[ks] = this.ddAngAlea(); } } // for ks this.nBlob = calculBlob( this.pt0, this.ang0, this.nbSegments, this.longueurs, this.dAngles, this.coeffsD, this.coeffsG, this); } // Nicolas.prototype.bouger // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Nicolas.prototype.dessiner = function() { // let nBlob=this.nBlob; let pbl; let debordH, debordD, debordB, debordG; // mémorise si on dépasse les bords let x0,y0,x1,y1,x2,y2,x3,y3; debordH=false; debordD=false; debordB=false; debordG=false; function testDebord (pt) { let x = pt[0]; let y = pt[1]; if ( y < 0 ) debordH = true; if ( x > maxx ) debordD = true; if ( y > maxy ) debordB = true; if ( x < 0 ) debordG = true; } // testDebord ctx.beginPath(); ctx.moveTo (nBlob[0][0],nBlob[0][1]); for (let ks = 0 ; ks < 2* this.nbSegments; ++ks) { pbl=this.nBlob[ks+1] ctx.bezierCurveTo (pbl[0][0], pbl[0][1], pbl[1][0], pbl[1][1], pbl[2][0], pbl[2][1]); testDebord(pbl[2]); } // tracé ctx.fillStyle=`hsl(${this.couleur},100%, 50%)`; ctx.fill(); // ctx.strokeStyle='#FFF'; // ctx.lineWidth=1; // ctx.stroke(); if (debordH) { ctx.beginPath(); ctx.moveTo (nBlob[0][0], nBlob[0][1] + maxy); for (let ks = 0 ; ks < 2* this.nbSegments; ++ks) { pbl=this.nBlob[ks+1] ctx.bezierCurveTo (pbl[0][0], pbl[0][1] + maxy, pbl[1][0], pbl[1][1] + maxy, pbl[2][0], pbl[2][1] + maxy); } // tracé ctx.fill(); // ctx.stroke(); } if (debordD) { ctx.beginPath(); ctx.moveTo (nBlob[0][0] - maxx, nBlob[0][1]); for (let ks = 0 ; ks < 2* this.nbSegments; ++ks) { pbl=this.nBlob[ks+1] ctx.bezierCurveTo (pbl[0][0] - maxx, pbl[0][1], pbl[1][0] - maxx, pbl[1][1], pbl[2][0] - maxx, pbl[2][1]); } // tracé ctx.fill(); // ctx.stroke(); } if (debordB) { ctx.beginPath(); ctx.moveTo (nBlob[0][0], nBlob[0][1] - maxy); for (let ks = 0 ; ks < 2* this.nbSegments; ++ks) { pbl=this.nBlob[ks+1] ctx.bezierCurveTo (pbl[0][0], pbl[0][1] - maxy, pbl[1][0], pbl[1][1] - maxy, pbl[2][0], pbl[2][1] - maxy); } // tracé ctx.fill(); // ctx.stroke(); } if (debordG) { ctx.beginPath(); ctx.moveTo (nBlob[0][0] + maxx, nBlob[0][1]); for (let ks = 0 ; ks < 2* this.nbSegments; ++ks) { pbl=this.nBlob[ks+1] ctx.bezierCurveTo (pbl[0][0] + maxx, pbl[0][1], pbl[1][0] + maxx, pbl[1][1], pbl[2][0] + maxx, pbl[2][1]); } // tracé ctx.fill(); ctx.stroke(); } } // Nicolas.prototype.dessiner // fin de la classe Nicolas // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // gestion animation // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - function clickCanvas() { arretAnim = !arretAnim; // on bascule la demande d'arrêt if (!arretAnim && arretAnimSync) { // si c'est un redémarrage arretAnimSync=false; // demande prise en compte suiteAnim(); // on continue l'animation } // clickCanvas() } // clickCanvas // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - function enchaine() { if (arretAnim && !arretAnimSync) { // demande d'arrêt arretAnimSync=true; // on l'enregistre suiteAnim=enchaine; // la reprise sera la présente fonction return; // on enregistre demande et on arrête } ctx.clearRect(0,0,maxx,maxy); for (let kn=0; kn< nbNic; kn++) { tbNic[kn].bouger() tbNic[kn].dessiner(); } // for kn // nNic2.bouger() // nNic2.dessiner(); setTimeout(enchaine,20); } // enchaine // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - //------------------------------------------------------------------------- function calculBlob( pt0, ang0, nSegments, longueurs, dAngles, coeffsD, coeffsG, nic) { /* un squelette étant donné par : - la position du point de référence - l'orientation du segment de référence - le nombre de points - un tableau [0..nSegments-1] de longueurs de segments - un tableau d'angles : dAngles[0..nSegments-2] sont les VARIATIONS d'angle à chaque changement de segment - coeffsD et coeffsG[0..nSegments-2], coefficients qui déterminent l'épaisseur du blob la fonction rend un tableau qui commence par le point du début du 1er segment, suivi de N tableaux de 3 points qui permettent de tracer des cubiques de Bézier qui forment une courbe fermée */ /* pour récupérer le qui existe déjà et qui calculait à partir d'une extrémité, on remonte du point central jusqu'au début du premier segment. Ensuite, on fait les calculs en suivant les segments, donc en refaisant le même chemin à l'envers. amélioration à apporter : exploiter les valeurs calculées pour éviter de recommencer les mêmes calculs. */ /* le paramètre nic a été ajouté ultérieurement, dans le seul but de mettre à jour nic.direction, avec la direction du dernier segment, pour donner l'impression de déplacement dans l'axe de la 'tête' */ let [x0, y0] = pt0; // point central // on va aller jusqu'à une extrémité let kSCou; if (nSegments & 1) { // nombre impair de segments, remonter de la moitié de sa longueur kSCou = mfloor(nSegments/2); let lng= longueurs[kSCou]/2; // pour remonter d'une demie longueur x0 -= lng * mcos(ang0); y0 -= lng * msin(ang0); ang0 -= dAngles[kSCou - 1]; // orientation du segment précédent kSCou--; } else { kSCou = (nSegments / 2) -1 } for ( ; kSCou >= 0 ; --kSCou) { x0 -= longueurs[kSCou] * mcos (ang0); y0 -= longueurs[kSCou] * msin (ang0); if (kSCou > 0) ang0 -= dAngles[kSCou-1]; } // ici, x0, y0 et ang0 positionnés à une extrémité du squelette let x1,y1; // fin du segment courant let pBlob=[]; let angCou = ang0; // direction du segment courant let angBiss; // direction de la bissectrice pBlob[0] = [x0, y0]; // on commence par le premier point ! // les intermédiaires maintenant for (let ks=0; ks < nSegments-1; ++ks) { let lng = longueurs[ks]; let x1 = x0 + lng * mcos(angCou); let y1 = y0 + lng * msin(angCou); let angBiss = angCou - mPIS2 + dAngles[ks]/2; // point 'à gauche' // distance prop. à la longueur let dg = longueurs[ks] * coeffsG[ks] ; pBlob[ks+1]= [x1 + dg * mcos(angBiss), y1 + dg * msin(angBiss)]; // point 'à droite' // distance prop. à la longueur let dd = longueurs[ks+1] * coeffsD[ks] ; pBlob[2 * nSegments - 1 - ks] = [x1 - dd * mcos(angBiss), y1 - dd * msin(angBiss)]; angCou += dAngles[ks]; x0=x1; y0=y1; } // for ks // on n'oublie pas le bout pBlob[nSegments] = [x0 + longueurs[nSegments-1] * mcos(angCou), y0 + longueurs[nSegments-1] * msin(angCou)]; pBlob.push(pBlob[0]); // recopie 3 points à la fin pBlob.push(pBlob[1]); // pour faciliter tracé pBlob.push(pBlob[2]); // pour faciliter tracé // pBlob calculé ci-dessus ne contient que les points par lesquels le tracé passera // nBlob calculé ci-après contient également les points de contr?le de Bézier pour ce tracé let nBlob=[[pBlob[1][0], pBlob[1][1] ]]; for (let ks = 0 ; ks < 2* nSegments; ++ks) { nBlob.push (calculerArc(pBlob[ks+0], pBlob[ks+1], pBlob[ks+2], pBlob[ks+3])); } nic.direction = angCou; return nBlob; } // calculBlob //------------------------------------------------------------------------- // début d'exécution { canv= document.createElement('canvas'); // c.style.backgroundColor='#FFE'; canv.style.position="absolute"; canv.style.top="8px"; canv.style.left="8px"; canv.addEventListener('click',clickCanvas); document.body.appendChild(canv); ctx=canv.getContext('2d'); } // création CANVAS Redemarrer(); }); // window load listener
粒子
时间
文字
hover
canvas
3d
游戏
音乐
火焰
水波
轮播图
鼠标跟随
动画
css
加载动画
导航
菜单
按钮
滑块
tab
弹出层
统计图
svg
×
Close
在线代码下载提示
开通在线代码永久免费下载,需支付20jQ币
开通后,在线代码模块中所有代码可终身免费下!
您已开通在线代码永久免费下载,关闭提示框后,点下载代码可直接下载!
您已经开通过在线代码永久免费下载
对不起,您的jQ币不足!可通过发布资源 或
直接充值获取jQ币
取消
开通下载
<!doctype html> <html> <head> <meta charset="utf-8"> <title>canvas草履虫-jq22.com</title> <script src="https://www.jq22.com/jquery/jquery-1.10.2.js"></script> <style>
</style> </head> <body>
<script>
</script>
</body> </html>
2012-2021 jQuery插件库版权所有
jquery插件
|
jq22工具库
|
网页技术
|
广告合作
|
在线反馈
|
版权声明
沪ICP备13043785号-1
浙公网安备 33041102000314号