Cet article présente principalement HTML5 pour vous apprendre à effectuer des changements d'images fragmentées (canvas). Il a une certaine valeur de référence
Avant-propos
Ancienne règle, le code source d'abord. La zone de l'image est cliquable et l'animation démarrera à partir de la position cliquée. À l'origine, j'ai créé cet effet il y a 3 ans, mais je pensais que c'était fait avec d'innombrables balises p. Les performances étaient un problème et il ne pouvait pas du tout fonctionner sur le terminal mobile. Récemment, sur un coup de tête, j'ai voulu apprendre à utiliser du CSS pur pour l'implémenter auprès d'un maître très doué en création de CSS. Cependant, je n'ai eu d'autre choix que d'abandonner car je n'avais pas assez de compétences, et finalement. a choisi d'utiliser une toile pour le compléter.Travail de préparation
1. Préparez d'abord plusieurs images de la même taille. Dans cet exemple, la taille de l'image est de 1920 * 1080 (remarque. : La taille ici est la taille de l'image originale, pas la taille affichée sur la page via CSS). Pour une utilisation ultérieure, ajoutez ces images à un élément masqué en HTML pour une utilisation ultérieure.<p class='hide'> <img class='img' src='./images/a.jpg' /> <img class='img' src='./images/b.jpg' /> <img class='img' src='./images/c.jpg' /> <img class='img' src='./images/d.jpg' /> <img class='img' src='./images/e.jpg' /> </p>
.hide { display: none; }
doit être cohérent avec l'image. les ressources ont le même rapport hauteur/largeur. Dans cet exemple, la taille du canevas est de 800 * 450.
<canvas id="myCanvas" width="800" height="450">您的浏览器不支持 CANVAS</canvas>
var ctx = document.querySelector('#myCanvas').getContext('2d'), img = document.querySelector('.img'); ctx.beginPath(); ctx.drawImage(img, 0, 0); ctx.closePath(); ctx.stroke();
Réaliser
Je crois que beaucoup de gens comprendront peu de temps après avoir lu ceci, cela consiste à utiliser plusieurs petites unités lorsqu'elles sont combinées ensemble, chaque unité n'est responsable que de dessiner une petite partie de l'image, et enfin de l'assembler pour former une image complète. Alors avant d'expliquer le code source en détail, passons en revue l'utilisation de la fonction drawImage dans Canvas. Puisque nous devons utiliser 9 paramètres de cette fonction et qu’il existe de nombreux paramètres, nous devons garder à l’esprit la signification de ces paramètres et des objets de référence.context.drawImage(img, sx, sy, swidth, sheight, x, y, width, height);
Grilles
Les grilles sont utilisées pour déterminer la taille d'une unité dans le canevas. Le plus important est que la taille de l'unité puisse être. modifié par l'écran. Divisible par la longueur des deux côtés, c'est-à-dire que la taille de l'unité doit être le diviseur commun de la largeur et de la hauteur de l'écran. Le diviseur commun n'est pas nécessairement le plus grand diviseur commun ou le plus petit diviseur commun, car s'il est trop grand, l'effet ne sera pas assez cool, et s'il est trop petit, les performances seront sous pression. Prenons la taille du plan de travail dans cet exemple comme 800 * 450. Je choisis ici 25 * 25 comme taille unitaire, c'est-à-dire que la toile entière est composée de 32 * 18, soit un total de 576 cellules. Après avoir divisé la grille, nous devons d’abord calculer certains paramètres de base.var imgW = 1920, //图片原始宽/高 imgH = 1080; var conW = 800, //画布宽/高 conH = 450; var dw = 25, //画布单元格宽/高 dh = 25; var I = conH / dh, //单元行/列数 J = conW / dw; var DW = imgW / J, //原图单元格宽/高 DH =imgH / I;
. Si cette situation est inversée, vous aurez des ennuis plus tard. Le dernier groupe de DW/DH est agrandi et converti à la taille de cellule de l'image d'origine, qui est utilisée ultérieurement pour recadrer l'image. 行数 = 画布高度 / 单元格高度;列数 = 画面宽度 / 单元格宽度
Dessiner
Étape par étape, nous dessinons d'abord la cellule du coin supérieur gauche. Parce que et 原图裁切位置
sont tous deux (0, 0), c'est le plus simple. 画布摆放位置
ctx.drawImage(img, 0, 0, DW, DH, 0, 0, dw, dh);
var i = 2, j = 3; ctx.drawImage(img, DW*j, DH*i, DW, DH, dw*j, dh*i, dw, dh);
. 裁剪或摆放的横坐标为单元格宽度 * 列号,纵坐标为单元格高度 * 行号
function handleDraw(img, i, j) { ctx.drawImage(img, DW*j, DH*i, DW, DH, dw*j, dh*i, dw, dh); }
ctx.beginPath(); for (var i = 0; i < I; i ++) { for (var j = 0; j < J; j ++) { handleDraw(img, i, j); } } ctx.closePath(); ctx.stroke();
完美~。其实到这一步核心部分就完成了,为什么呢?因为此时这幅图片已经是由几百个单元格拼合而成的,大家可以凭借天马行空的想像赋予其动画效果。
在分享自己的动画算法之前,先给大家看下拼错是什么样的~
还有点炫酷~
动画算法
下面分享下我的动画算法。Demo 里的效果是怎么实现的呢?
由于在画布的网格上,每个单元格都有行列号(i,j)。我希望能给出一个坐标(i,j)后,从近到远依次得出坐标周围所有菱形上的点。具体如下图,懒得做图了~
比如坐标为(3,3)
距离为 1 的点有(2,3)、(3,4)、(4,3)、(3,2)共4个;
距离为 2 的点有(1,3)、(2,4)、(3,5)、(4,4)、(5,3)、(4,2)、(3,1)、(2,2)共8个;
........
求出这一系列点的算法也很容易, 因为菱形线上的点与坐标的 行号差值的绝对值 + 列号差值的绝对值 = 距离
,具体如下:
function countAround(i, j, dst) { var resArr = []; for (var m = (i-dst); m <= (i+dst); m++) { for (var n = (j-dst); n <= (j+dst); n++) { if ((Math.abs(m-i) + Math.abs(n-j) == dst)) { resArr.push({x: m, y: n}); } } } return resArr; }
该函数用于给定坐标和距离(dst),求出坐标周围该距离上的所有点,以数组的形式返回。但是上面的算法少了边界限制,完整如下:
countAround(i, j, dst) { var resArr = []; for (var m = (i-dst); m <= (i+dst); m++) { for (var n = (j-dst); n <= (j+dst); n++) { if ((Math.abs(m-i) + Math.abs(n-j) == dst) && (m >=0 && n >= 0) && (m <= (I-1) && n <= (J-1))) { resArr.push({x: m, y: n}); } } } return resArr; }
这样我们就有了一个计算周围固定距离上所有点的纯净函数,接下来就开始完成动画渲染了。
首先编写一个用于清除单元格内容的清除函数,只需要传入坐标,就能清除该坐标单元格上的内容,等待之后绘制新的图案。
handleClear(i, j) { ctx.clearRect(dw*j, dh*i, dw, dh); }
anotherImg 为下一张图,最后通过 setInterval 不断向外层绘制新的图片完成碎片式的渐变效果。
var dst = 0, intervalObj = setInterval(function() { var resArr = countAround(i, j, dst); resArr.forEach(function(item, index) { handleClear(item.x, item.y); handleDraw(anotherImg, item.x, item.y); }); if (!resArr.length) { clearInterval(intervalObj); } dst ++; }, 20);
当 countAround 返回的数组长度为0,即到坐标点该距离上的所有点都在边界之外了,就停止定时器循环。至此所有核心代码已经介绍完毕,具体实现请查看源码。
现在给定画布上任意坐标,就能从该点开始向四周扩散完成碎片式的图片切换效果。
在自动轮播时,每次从预设好的8个点(四个角及四条边的中点)开始动画,8个点坐标如下:
var randomPoint = [{ x: 0, y: 0 }, { x: I - 1, y: 0 }, { x: 0, y: J - 1 }, { x: I - 1, y: J - 1 }, { x: 0, y: Math.ceil(J / 2) }, { x: I - 1, y: Math.ceil(J / 2) }, { x: Math.ceil(I / 2), y: 0 }, { x: Math.ceil(I / 2), y: J - 1 }]
点击时,则算出点击所在单元格坐标,从该点开始动画。
function handleClick(e) { var offsetX = e.offsetX, offsetY = e.offsetY, j = Math.floor(offsetX / dw), i = Math.floor(offsetY / dh), //有了i, j,开始动画... },
目前该效果只是 Demo 阶段,有空的话会将该效果插件化,方便有兴趣的朋友使用。
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!