Maison > Article > interface Web > Apprendre en jouant avec HTML5 (6) - Introduction détaillée aux Autobots et aux transformations
1. L'État et sa préservation et sa restauration
Avant de commencer cette section, nous devons d'abord comprendre ce qu'est l'état et la préservation et la restauration de l'état. Les personnes qui ont joué avec la programmation MFC rencontrent souvent du code comme celui-ci :
pOldPen=pDC->SelectObject(pNewPen)
Lorsque nous sélectionnons un nouvel objet pinceau, nous devons toujours le sauvegarder. Vivez le vieil objet pinceau, pourquoi voulez-vous faire ça ? Étant donné que le nouvel objet pinceau n'est destiné qu'à un usage temporaire, lorsqu'il est épuisé et que nous souhaitons restaurer la configuration d'origine du pinceau, si l'ancienne configuration n'est pas enregistrée à l'avance, ces configurations seront perdues et il n'y a aucun moyen de la restaurer.
Dans le dessin HTML5, l'état à un certain moment est la valeur de configuration d'une série de propriétés de l'objet contextuel à l'instant présent. Cependant, il existe relativement peu de propriétés qui déterminent l'état d'un pinceau, comme par exemple. comme la couleur, l'épaisseur, le type de ligne, etc., et il existe de nombreux attributs qui déterminent l'état du contexte, notamment les suivants :
1 La configuration du mouvement, de la rotation et de la mise à l'échelle de l'objet contextuel actuel
.2. Le StrokeStyle, fillStyle, globalAlpha, de l'objet contextuel actuel, lineWidth, lineCap, lineJoin, miterLimit, shadowOffsetX, shadowOffsetY, shadowBlur, shadowColor, globalCompositeOperation valeurs d'attribut
3. objet de contexte actuel
La série de configurations ci-dessus détermine le moment actuel. L'état de l'objet de contexte, y compris le mouvement, la rotation, la mise à l'échelle, l'opération globaleComposite (combinaison) et le recadrage dont nous parlerons. ça bientôt.
2. Sauver et restaurer l'État
Nous avons dit ci-dessus que l'État à un certain moment est déterminé par tant d'attributs si nous voulons sauver l'État à ce moment-là. Pour le moment, il faut utiliser ces attributs. Ce serait trop gênant de sauvegarder les valeurs une par une et de les réinitialiser une par une lors de la restauration. C'est effectivement le cas, donc le contexte propose deux méthodes simples pour sauvegarder et restaurer l'état. Ce sont :
save( ) et restaurer( )
save et restaurer La méthode peut être appelée. plusieurs fois. Chaque fois que la méthode save est appelée, l'état au moment de l'appel (c'est-à-dire une série de valeurs d'attribut) est placé dans une pile.
Chaque fois que la méthode de restauration est appelée, l'état de la dernière sauvegarde est restauré, c'est-à-dire retiré de la pile.
Imaginez un chargeur. La première balle tirée est toujours la dernière enfoncée dans le chargeur.
3. Variation
1. Déplacer : traduire(dx,dy)
cela La méthode semble très simple, mais en fait elle contient certaines significations mathématiques. Vous pouvez penser que l'origine de l'ensemble du système de coordonnées a été déplacée. Tout point (x, y) dans le nouveau système de coordonnées est équivalent aux coordonnées du système de coordonnées d'origine. system :
x'=x+dx
y'=y+dy
Si nous appelons ctx.translate(5,8) pour changer l'état du système de coordonnées de l'objet contextuel , alors dans le nouvel état Dessiner au point (3,2) équivaut à l'image dessinée au point (8,10) dans l'état d'origine, c'est-à-dire
x'=5+3=8
y'=5+ 2=10
Peut-être vous demanderez-vous, pourquoi s'embêter autant ? Est-il acceptable de faire la comparaison directement à (8,10) ? Par exemple, remplacez
ctx.translate(5,8)
ctx.drawImage(img,3,2)
par
ctx.drawImage(img, 8,10)
N’est-ce pas plus simple et plus direct ?
Je crois comprendre que lorsque le mouvement est important, il sert à d'autres transformations graphiques. Changer de manière appropriée l'origine des coordonnées peut faciliter la compréhension des calculs graphiques et apporter une grande commodité. Un exemple simple, si. :
a un segment de droite, qui est un petit segment dans le sens positif de l'axe des x
y = 0 (1 <= x <= 3),
Si vous prenez l'origine des coordonnées comme centre du cercle et que vous le faites pivoter de 90 degrés dans le sens inverse des aiguilles d'une montre, le segment de ligne coïncidera avec la direction positive de l'axe y. Le segment de ligne pivoté est :
x = 0 (1 <= y <= 3)Mais il nous est impossible de tourner avec l'origine comme centre du cercle à chaque fois si nous tournons avec un seul point final (1, 0) du segment de droite comme centre du cercle, comment pouvons-nous obtenir la valeur des coordonnées de chaque point sur le segment de droite après rotation ? En fait, ce processus peut être divisé en trois étapes : Étape 1 :Déplacez la coordonnée d'origine vers (1,0). Le nouveau segment de ligne est toujours sur l'axe des x, mais l'équation devient : y = 0 (0 <= x <= 2)
Étape 2 :rotation avec l'origine du nouveau système de coordonnées comme centre du cercle, et obtenez le segment de droite x = 0 sous le nouveau système de coordonnées (0 <= y <= 2)
Étape 3 : Déplacez l'originedu nouveau système de coordonnées vers (- 1,0) sous le nouveau système de coordonnées, c'est-à-dire restaurer l'origine À la position d'origine, le segment de ligne à ce moment est : x = 1 (0 <= y <= 2)
Le Le segment de ligne obtenu à la troisième étape est le segment de ligne final qui doit être dessiné. À partir de cet exemple, nous pouvons voir que même dans un cas aussi simple, il est difficile de calculer directement la figure pivotée sans déplacer l'origine des coordonnées.Conseils : Avant de déplacer l'origine des coordonnées, n'oubliez pas de sauvegarder l'état. Bien sûr, ne restaurez pas l'état après le dessin.
2. Mise à l'échelleéchelle(sx, sy)
C'est également très simple sx, sy sont des facteurs d'échelle et les nouvelles coordonnées. après mise à l'échelle Tout point (x, y) du système est équivalent aux coordonnées dans le système de coordonnées d'origine : x' = x * sxy' = y * sy
De même, N'oubliez pas de sauvegarder et de restaurer l'état lors du changement de système de coordonnées
3. Rotationrotation(A)
l'angle est l'angle de rotation, tout point (x, y) dans le nouveau système de coordonnées après la rotation est équivalent aux coordonnées dans le système de coordonnées d'origine :
x' = x cosA - y sinA
y ' = x sinA + y cosA
De même, N'oubliez pas de toujours enregistrer et restaurer l'état lors du changement de système de coordonnées
4. (m11, m12, m21, m22, dx, dy)
En fait, le mouvement, la mise à l'échelle et la rotation mentionnés précédemment sont des cas particuliers de transformation. Les six paramètres de la méthode de transformation forment une transformation. matrice, comme suitAppeler la méthode de transformation équivaut à utiliser ces paramètres pour définir une nouvelle matrice de transformation pour l'objet contextuel. Pour le contenu spécifique de la matrice de transformation, veuillez vous référer à Graphiques Informations associées, voici quelques cas particuliers simples :
Déplacer traduire(dx,dy) : équivalent à transformer(1,0,0,1 ,dx,dy)
Echelle scale(sx,xy) : équivalent à transform(sx,0,0,sy,0,0)
rotate(A) : Équivalent à transform(cosA,sinA,-sinA,cosA,0,0)
Utiliser (dx,dy) comme point de référence pour faire pivoter l'angle A : transformer (cosA, sinA, -sinA, cosA, dx(1-cosA) + dysinA, dy(1-cosA) - dxsinA)
Utiliser (dx,dy) comme point de référence pour effectuer une mise à l'échelle (sx,sy) Mise à l'échelle : transform(sx, 0, 0, sy, dx(1-sx), dy(1-sy))
Il existe de nombreux autres Pour des transformations plus complexes, vous pouvez vous référer aux informations relatives aux graphiques. Ce qui suit est un exemple de déformation du point de référence. Si la souris est maintenue enfoncée sur un certain point de l'image, l'image sera mise à l'échelle ou pivotée en utilisant ce point comme point de référence. après avoir relâché le bouton. Astuce : Cet exemple de mise à l'échelle et de rotation n'utilise pas de matrice de déformation, mais est composé de quatre déformations simples. L'effet est le suivant :Déplacer - maintenez la souris enfoncée sur l'image et déplacez-la
Mise à l'échelle du point de référence - maintenez la souris enfoncée à un certain point de l'image
Point de référence rotation - à un certain point de l'image Appuyez et maintenez la souris à un certain point
Le point de référence zoome et tourne en même temps - appuyez et maintenez la souris à un certain point de l'image
4. Combinaison
La soi-disant combinaison est l'effet qui apparaîtra lorsqu'un graphique est dessiné sur un autre graphique. Par défaut, le graphique supérieur recouvre le graphique inférieur, appelé source-over. L'objet contextuel a un total de douze types de combinaison. L'attribut globalCompositeOperation est utilisé pour définir le type de combinaison, comme suit : globalCompositeOperation = typetype est l'un des types de combinaison. 12 valeurs de chaîne suivantes 1 :Remarque : Dans tous les exemples ci-dessus, le carré bleu est dessiné en premier, c'est-à-dire le "contenu du canevas existant" . Le cercle rouge est le dessiné après , qui est la "nouvelle forme".
5. Tracé de détourageDans le premier article, nous avons présenté deux principaux types de méthodes de dessin pour les objets contextuels, à savoir le
trait série de méthodes et la série de méthodesfill qui remplissent la zone. En fait, l'objet contextuel possède également un type de méthode de dessin appelée clipping clip. Qu'est-ce que le recadrage ? Pour utiliser une analogie inappropriée, vous couvrez l’écran du téléviseur avec un morceau de tissu. À ce stade, vous ne pouvez voir aucun changement sur l’écran du téléviseur.
Cependant, si vous découpez une zone sur le tissu, vous pouvez au moins voir les changements d'écran dans cette zone. Bien sûrl'écran en dehors de la zone de recadrage change également constamment (c'est-à-dire qu'il est également redessiné), mais vous ne pouvez pas le voir
. C'est ce qu'on appelle le recadrage, et vous rencontrez souvent ce besoin lors du traitement des images.Alors, quel est le chemin de détourage
? Il est dit que vous devez découper une zone sur le tissu. Comment est née cette zone ?Cette zone est définie par le chemin de dessin avant l'action de découpage clip
Il peut s'agir d'un carré, d'un cercle, d'une forme à cinq étoiles ou de toute autre forme de contour pouvant être dessinée.Ainsi, le chemin de détourage est en fait le chemin de dessin, mais ce chemin n'est pas utilisé pour dessiner, mais pour définir une ligne de démarcation entre la zone d'affichage et la zone d'occlusion.
Si vous ne comprenez pas ce qu'est un chemin de dessin, il est présenté dans l'article précédent HTML5 Apprendre en jouant (2) : Dessin de base. L'exemple suivant utilise deux méthodes de recadrage. La première méthode affiche une zone de recadrage circulaire qui se déplace d'avant en arrière. Le processus général est le suivant : 1. Effacez la toile 2. Changez la position centrale du cercle.
3. Dans le nouveau Définissez une zone de recadrage circulaire au centre du cercle 4. Dessinez une image de beauté sur la toile Puisque nous continuons à définir la zone de recadrage à de nouvelles positions , on peut voir la zone de recadrage Lors du déplacement, l'image en dehors de la zone de recadrage ne s'affiche pas Nous utilisons la méthode clip pour définir la zone de recadrage. Les graphiques dessinés ensuite ne peuvent afficher qu'une partie de la zone de recadrage, et. le canevas est toujours affiché en dehors de la couleur d’arrière-plan de recadrage. Si vous ne souhaitez pas bloquer complètement l'image en dehors de la zone de recadrage, par exemple, nous voulons que l'image dans la zone de recadrage soit entièrement affichée, mais l'image en dehors de la zone de recadrage est affichée dans un semi -de manière transparente, que faire ? Cela nécessite les connaissances combinées que nous avons mentionnées ci-dessus. La deuxième méthode affiche une occlusion translucide. L'idée générale est la suivante : 1. Effacer la toile 2. Remplissez toutes les zones de la toile avec une couleur translucide. , avec transparence 0,93 Changer la position centrale du cercle4、在新的圆心位置处以 XOR 方式绘制圆,这样和圆形重叠的部分将被擦除掉。
这时候我们得到的图形效果是一个半透明的画布,上面有一块完全透明的圆形区域
5、 在第 4 步的基础上,以 destination-over 方式绘制美女图像,这时候美女图像将会出现在第 4 步图形效果的下方,想象一下,正好是我们想要的效果吧?!
代码 Code highlighting produced by Actipro CodeHighlighter (freeware) http://www.CodeHighlighter.com/ --><canvas id="canvas1" width="250" height="300" onmousedown="trans.transform(event);" onmouseup="trans.init(event);" onmousemove="trans.translate(event);" style="background-color:black"> 你的浏览器不支持 <canvas>标签,请使用 Chrome 浏览器 或者 FireFox 浏览器 </canvas><br/> <input type="radio" name="r" id="r1" checked="checked">移动——在图像上按住鼠标并移动<br /> <input type="radio" name="r" id="r2">基准点缩放——在图像某点处按住鼠标<br /> <input type="radio" name="r" id="r3">基准点旋转——在图像某点处按住鼠标<br /> <input type="radio" name="r" id="r4">基准点缩放同时旋转——在图像某点处按住鼠标<br /> <canvas id="canvas3" width="250" height="300" style="background-color:black"> 你的浏览器不支持 <canvas>标签,请使用 Chrome 浏览器 或者 FireFox 浏览器 </canvas><br/> <input type="button" onclick="move(1);" value="移动裁剪区域"> <input type="button" onclick="move(2);" value="移动蒙版"> <input type="button" onclick="stop();" value="停止移动"><br /> <div> <table> <tr> <td><canvas id="tut0" width="125" height="125"></canvas><br/><label id="lab0"></label></td> <td><canvas id="tut1" width="125" height="125"></canvas><br/><label id="lab1"></label></td> <td><canvas id="tut2" width="125" height="125"></canvas><br/><label id="lab2"></label></td> <td><canvas id="tut3" width="125" height="125"></canvas><br/><label id="lab3"></label></td> </tr> <tr> <td><canvas id="tut4" width="125" height="125"></canvas><br/><label id="lab4"></label></td> <td><canvas id="tut5" width="125" height="125"></canvas><br/><label id="lab5"></label></td> <td><canvas id="tut6" width="125" height="125"></canvas><br/><label id="lab6"></label></td> <td><canvas id="tut7" width="125" height="125"></canvas><br/><label id="lab7"></label></td> </tr> <tr> <td><canvas id="tut8" width="125" height="125"></canvas><br/><label id="lab8"></label></td> <td><canvas id="tut9" width="125" height="125"></canvas><br/><label id="lab9"></label></td> <td><canvas id="tut10" width="125" height="125"></canvas><br/><label id="lab10"></label></td> <td><canvas id="tut11" width="125" height="125"></canvas><br/><label id="lab11"></label></td> </tr> </table> </div> <script type="text/javascript"> //美女图的 Base64 编码 IMG_SRC='data:image/gif;base64,/9j/4QDfRXhpZgAASUkqAAgAAAAFABIBAwA......';//省略四十字节 //========================================== //基准点变形类 //========================================== function Transform(){ //获取画布对象 this.ctx = document.getElementById("canvas1").getContext("2d"); //创建图像对象 this.img=new Image(); //指定图像源 this.img.src=IMG_SRC; this.interval = null; //鼠标按钮状态 this.pressed=false; this.init(); } //初始化图形 Transform.prototype.init=function(){ //鼠标按钮状态 this.pressed=false; //停止计时器 if(this.interval) clearInterval(this.interval); //变化值 this.delta = 0.06; //清空 this.ctx.clearRect(0,0,250,300); //重绘 this.paint(); } //绘制图像 Transform.prototype.paint=function(){ var that=this; var img=this.img if(img.complete) that.ctx.drawImage(img,0,0); else var interval = setInterval(function(){ if(img.complete){ that.ctx.drawImage(img,0,0); clearInterval(interval); } },300); } //鼠标按钮按下后,开始变形 Transform.prototype.transform = function(){ //获取基准点 this.dx=event.offsetX; this.dy=event.offsetY; //获取基准点 this.startx=event.offsetX; this.starty=event.offsetY; //初始缩放比例 this.sc=1; //初旋转角度 this.angle=0; var that=this; if(document.getElementById("r1").checked) //鼠标按钮状态 this.pressed=true; else if(document.getElementById("r2").checked) this.interval = setInterval(function(){that.scale()},50); else if((document.getElementById("r3").checked)) this.interval = setInterval(function(){that.rotate()},50); else this.interval = setInterval(function(){that.scaleAndRotate()},50); } //移动 Transform.prototype.translate = function(){ this.ddx=event.offsetX-this.startx; this.ddy=event.offsetY-this.starty; if(this.pressed){ //清空 this.ctx.clearRect(0,0,250,300); //保存状态 this.ctx.save(); //平移 this.ctx.translate(this.ddx,this.ddy); //重绘 this.paint(); //绘制基准点 this.ctx.fillStyle="red"; this.ctx.fillRect(this.dx-5,this.dy-5,10,10); //恢复状态 this.ctx.restore(); } } //缩放变形 Transform.prototype.scale = function(){ //清空 this.ctx.clearRect(0,0,250,300); //改变缩放比例 this.sc=this.sc - this.delta; if(this.sc<0.2 || this.sc>2) this.delta = -this.delta; //保存状态 this.ctx.save(); //以 (dx,dy) 为基准点进行 (sx,sy)比例缩放:transform(sx, 0, 0, sy, dx(1-sx), dy(1-sy)) this.ctx.transform(this.sc, 0, 0, this.sc, this.dx*(1-this.sc), this.dy*(1-this.sc)) //用新的变形矩阵重绘 this.paint(); //绘制基准点 this.ctx.fillStyle="red"; this.ctx.fillRect(this.dx-5,this.dy-5,10,10); //恢复状态 this.ctx.restore(); } //旋转变形 Transform.prototype.rotate = function(){ //清空 this.ctx.clearRect(0,0,250,300); //改变缩放比例 var PI = Math.PI; this.angle=this.angle + PI/60; //保存状态 this.ctx.save(); //以 (dx,dy) 为基准点旋转角度 A:transform(cosA, sinA, -sinA, cosA, dx(1-cosA) + dysinA, dy(1-cosA) - dxsinA) this.ctx.transform(Math.cos(this.angle), Math.sin(this.angle), -Math.sin(this.angle), Math.cos(this.angle), this.dx*(1-Math.cos(this.angle)) + this.dy*Math.sin(this.angle), this.dy*(1-Math.cos(this.angle)) - this.dx*Math.sin(this.angle)) //用新的变形矩阵重绘 this.paint(); //绘制基准点 this.ctx.fillStyle="red"; this.ctx.fillRect(this.dx-5,this.dy-5,10,10); //恢复状态 this.ctx.restore(); } //即缩放又旋转变形,没有使用变形矩阵 Transform.prototype.scaleAndRotate = function(){ //清空 this.ctx.clearRect(0,0,250,300); //改变缩放比例 this.sc=this.sc - this.delta; if(this.sc<0.2 || this.sc>2) this.delta = -this.delta; var PI = Math.PI; this.angle=this.angle + PI/60; //保存状态 this.ctx.save(); //先移动原点到基点 this.ctx.translate(this.dx,this.dy); this.ctx.scale(this.sc,this.sc); this.ctx.rotate(this.angle); this.ctx.translate(-this.dx,-this.dy); //用新的变形矩阵重绘 this.paint(); //绘制基准点 this.ctx.fillStyle="red"; this.ctx.fillRect(this.dx-5,this.dy-5,10,10); //恢复状态 this.ctx.restore(); } var trans = new Transform(); //========================================== function Clip(){ var canvas = document.getElementById("canvas3"); this.ctx = canvas.getContext("2d"); this.img=new Image(); this.img.src=IMG_SRC; //移动方向 this.delta=[3,3]; //起始点 this.pos_x = 225; this.pos_y = 120; //半径 this.radius = 40; //画布的长和宽 this.w = parseInt(canvas.getAttribute("width")); this.h = parseInt(canvas.getAttribute("height")); } Clip.prototype.draw1=function(){ //碰撞检测 if (this.pos_x < this.radius) { this.delta[0] = Math.random() % 4 + 5; } else if (this.pos_x > this.w - this.radius) { this.delta[0] = -(Math.random() % 4 + 5); } if (this.pos_y < this.radius) { this.delta[1] = Math.random() % 4 + 5; } else if (this.pos_y > this.h - this.radius) { this.delta[1] = -(Math.random() % 4 + 5); } this.pos_x += this.delta[0]; this.pos_y += this.delta[1]; this.ctx.clearRect(0, 0, this.w, this.h); //保存状态 this.ctx.save() //移动变形 this.ctx.translate(this.pos_x,this.pos_y); //设置裁剪区域 this.ctx.beginPath(); this.ctx.arc(0 ,0,this.radius,0,Math.PI*2,true); this.ctx.clip(); // 将图片画到画布上 this.ctx.drawImage(this.img, -this.pos_x, -this.pos_y,this.w, this.h); //恢复状态 this.ctx.restore(); } Clip.prototype.draw2=function(){ //碰撞检测 if (this.pos_x < this.radius) { this.delta[0] = Math.random() % 4 + 5; } else if (this.pos_x > this.w - this.radius) { this.delta[0] = -(Math.random() % 4 + 5); } if (this.pos_y < this.radius) { this.delta[1] = Math.random() % 4 + 5; } else if (this.pos_y > this.h - this.radius) { this.delta[1] = -(Math.random() % 4 + 5); } this.pos_x += this.delta[0]; this.pos_y += this.delta[1]; this.ctx.clearRect(0, 0, this.w, this.h); //绘制灰色的半透明蒙版 this.ctx.fillStyle="rgba(125,125,125,0.9)" this.ctx.fillRect(0, 0, this.w, this.h); //保存状态 this.ctx.save() //移动坐标 this.ctx.translate(this.pos_x,this.pos_y); //裁剪透明的圆形区域 this.ctx.globalCompositeOperation = "xor"; this.ctx.fillStyle="white" this.ctx.beginPath(); this.ctx.arc(0 ,0,this.radius,0,Math.PI*2,true); this.ctx.fill(); // 将图片画到蒙版的下面,即只露出透明区域 this.ctx.globalCompositeOperation = "destination-over"; this.ctx.drawImage(this.img, -this.pos_x, -this.pos_y,this.w, this.h); //恢复状态 this.ctx.restore(); } var cl=new Clip(); cl.interval=null; function move(id){ if(cl.interval) clearInterval(cl.interval) if(id==1){ cl.ctx.clearRect(0, 0, 450, 300); cl.interval=setInterval(function(){cl.draw1()},20); } else{ cl.ctx.clearRect(0, 0, 450, 300); cl.interval=setInterval(function(){cl.draw2()},20); } } function stop(){ clearInterval(cl.interval) } var compositeTypes = [ 'source-over','source-in','source-out','source-atop', 'destination-over','destination-in','destination-out','destination-atop', 'lighter','darker','copy','xor' ]; function drawComp(){ for (i=0;i<compositeTypes.length;i++){ var label = document.createTextNode(compositeTypes[i]); document.getElementById('lab'+i).appendChild(label); var ctx = document.getElementById('tut'+i).getContext('2d'); // draw rectangle ctx.fillStyle = "#09f"; ctx.fillRect(15,15,70,70); // set composite property ctx.globalCompositeOperation = compositeTypes[i]; // draw circle ctx.fillStyle = "#f30"; ctx.beginPath(); ctx.arc(75,75,35,0,Math.PI*2,true); ctx.fill(); } } drawComp(); </script>
Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!