CSSDesignAwards를 검색하다가 이미지 내용을 분할하는 효과를 발견했습니다(웹사이트: https://weareludwig.com). 클릭해서 보실 수 있습니다. 너무 멋있어서 직접 구현해 봤습니다. 효과는 꽤 좋았습니다. 효과 확인하기 https://codepen.io/geeknoble/pen/OQaOVG
분석하다첫째, 그림의 내용이 작은 직사각형으로 나누어져 있고 각 직사각형이 무작위로 이동되어 있음을 알 수 있습니다. Canvas의 drawImage 함수는 이미지 내용을 잘라서 Canvas 캔버스에 그릴 수 있으므로 이 효과의 주요 구현 원리는 drawImage를 사용하는 것입니다. 두 가지 주요 효과가 있는데, 하나는 그림 내용을 방해하고 복원하는 것이고, 다른 하나는 다음 그림으로 전환하는 것입니다. DrawImage는 두 가지 효과 모두에 사용할 수 있지만 이동 거리가 다릅니다. 일반적인 아이디어가 있으면 구현을 시작할 수 있습니다.
초기 작업먼저 그림의 너비와 높이, 직사각형 수, 절단 크기 등과 같은 일부 변수를 초기화한 다음 각 직사각형의 좌표를 계산하고 이중 루프를 사용하여 직사각형 좌표를 데이터에 저장해야 합니다. . 각 직사각형에는 무작위 변위가 있습니다. 이 변위도 무작위로 저장되고 저장되어야 합니다. 그 중 x와 y는 캔버스 캔버스의 좌표를 나타내고, x1과 y1은 이미지 크롭의 좌표를 나타냅니다.
init: function (context, width, height, Area, img) { this.context = context; this.img = img; this.imgWidth = img[0].width; //사진 너비 및 높이 this.imgHeight[ 0 ].height; this.index = 0; //현재 사진 번호 this.width = width; //캔버스 너비 및 높이 this.height = height/12; //작은 직사각형의 길이 this.countX = width / this.area; //가로 및 세로 방향의 작은 직사각형의 개수 this.countY = height / this.area; this.wx = this.imgWidth / this .countX; //작은 직사각형의 너비와 높이 this.wy = this.imgHeight / this.countY; //사진 상태, true는 분할되지 않음을 의미합니다. this.dataFlag = true; 좌표 상태, true는 this.duration이 추가되는 임의의 값이 없음을 의미합니다. = 1000; //애니메이션 시간 this.duration2 = 0; this.data = []; //작은 직사각형 좌표 정보 this.randoms = []; //직사각형 좌표 초기화 x1 = 0, y1 = 0, x = 0, y = 0; for (var i = 0; i < this.countY; i++) { for (var j = 0; j < this.countX; j++) { context.drawImage(this.img[this.index], x1, y1, this.wx, this.wy, x, y, this.area, this.area); this.data.push({ x1: x1, y1: y1, x: x, y: y }); //임의의 값 추가 this.randoms.push(random(-this.area, this.area)); x1 += this.wx x += this.area; wy; x = 0; y += this.area } this.checkMargin() }가장자리 감지
직사각형에 변위를 추가하기 전에 변위된 좌표가 그림의 한계를 초과하는지 여부를 확인해야 합니다. 예를 들어 상단의 직사각형이 y축으로 이동하는 경우 위쪽으로만 이동할 수 있는지 여부입니다. 현재 좌표에 변위 값을 더한 값이 0보다 작거나 이미지의 너비와 높이보다 큽니다. 업데이트된 좌표가 0보다 작으면 임의의 값은 음수여야 하며, 임의의 값이 그림의 높이보다 크면 음수로 변경해야 합니다. 각 직사각형은 한 방향으로 움직이기 때문에 여기에서는 짝수 비트가 x축을 이동하고 홀수 비트가 y축을 이동한다고 씁니다.
//가장자리 감지 checkMargin: function () { var self = this; this.data.forEach(function (item, index) { if (index % 2 == 0) { // 인덱스가 a일 때 x축 이동 2의 배수, 그렇지 않으면 y축 이동 if (item.x1 + self.randoms[index] < 0) // 양수로 변경 self.randoms[index] = -self.randoms[index] if (item .x1 + self.wx + self.randoms[index] > self.imgWidth ) // 음수로 변경 self.randoms[index] = -Math.abs(self.randoms[index]) } else { if (item.y1 + self .randoms[index] < 0) self.randoms[index] = -self.randoms[index]; if (item.y1 + self.randoms[index] + self.wy > self.imgHeight) self.randoms[index] = -Math.abs(self.randoms[index]) } }) }분리와 회복
애니메이션 콘텐츠의 분리 및 복원은 직사각형 좌표의 값을 업데이트하는 것입니다. 콘텐츠를 방해하려면 데이터의 좌표에 임의의 값을 추가하면 되고, 복원은 임의의 값을 빼는 것입니다.
//가장자리 감지 checkMargin: function () { var self = this; this.data.forEach(function (item, index) { if (index % 2 == 0) { // 인덱스가 a일 때 x축 이동 2의 배수, 그렇지 않으면 y축 이동 if (item.x1 + self.randoms[index] < 0) // 양수로 변경 self.randoms[index] = -self.randoms[index] if (item .x1 + self.wx + self.randoms[index] > self.imgWidth ) // 음수로 변경 self.randoms[index] = -Math.abs(self.randoms[index]) } else { if (item.y1 + self .randoms[index] < 0) self.randoms[index] = -self.randoms[index]; if (item.y1 + self.randoms[index] + self.wy > self.imgHeight) self.randoms[index] = -Math.abs(self.randoms[index]) } }) }
좌표를 저장한 후 이동 프로세스가 원활하게 전환되도록 할 수 있습니다. Tween.js의 여유 알고리즘을 사용할 수 있습니다. 이 알고리즘에는 현재 시간, 초기 위치 및 종료 위치가 있습니다. 애니메이션 시간. 자세한 내용은 Zhang Xinxu의 기사 https://www.zhangxinxu.com/wordpress/2016/12/how-use-tween-js-animation-easing/을 참조하세요. Tween.js를 사용하여 각 프레임에서 이동할 거리를 계산한 다음 requestAnimationFrame을 사용하여 좌표를 업데이트할 수 있습니다.
blockAnimation: function () { var flag = 1; if (this.state) { // 이미지를 중단할지 아니면 이미지를 복원할지 결정 this.update(true) } else { flag = -1; ; } var self = this; this.startTime = +new Date(); // 현재 시간 가져오기 this.state = !this.state (function animation() { var t = +new Date(); >= self.startTime + self.duration) { // 애니메이션 종료 조건 return false; } self.data.forEach(function (item, index) { if (index % 2 == 0) { var pos = Math.tween.Expo. easyInOut(t - self.startTime, 0, self.randoms[index] * 플래그, self.duration); self.context.drawImage(self.img[self.index], item.x1 + pos, item.y1, self.wx, self.wy, item.x, item.y, self. 각 프레임에서 이동한 거리를 계산합니다. Area, self.area); } else { var pos = Math.tween.Expo.easeInOut(t - self.startTime, 0, self.randoms[index] * 플래그, self.duration); self.context.drawImage(self.img[self.index], item.x1, item.y1 + pos, self.wx, self.wy, item.x, item.y, self.area, self.area); } }); requestAnimationFrame(애니메이션) })();
이 시점에서 분리와 복원의 애니메이션이 구현되었습니다.
사진 전환다음으로 이미지 전환 부분을 처리하기 시작합니다. 이는 캐러셀 이미지와 약간 비슷합니다. 캐러셀 이미지 애니메이션은 시각적 창의 너비만큼 각 이미지의 위치를 이동합니다. 여기서도 이미지에 좌표를 추가하면 됩니다. y를 달성하기 위한 높이 축을 전환합니다. 캐러셀 사진과 다르게 여기에는 캔버스 태그가 하나만 있습니다. 전환 시 현재 사진과 다음 사진의 좌표만 변경하면 됩니다. 현재 사진의 이동 거리는 y1 + pos이고, 다음 그림은 y1 + pos - imgHeight입니다(imgHeight를 줄여야 하는 이유는 말할 필요도 없습니다).
//수직 슬라이딩 애니메이션 VerticalAnimation: function (val) { if (!this.time2) { return false } this.checkTime(2) var self = this ? val = 1; 위 또는 아래로 슬라이드합니다. if ((this.index + val) < 0 || (this.index + val) >= (this.img.length)) { // 사진 일련번호가 끝인지 확인 return false } 이.상태 ? this.update(true) : this.update(false); this.startTime = +new Date(); (function animation() { var t = +new Date(); if (t >= self.startTime + self .duration2) { val === 1 ? self.index++ : self.index--; //그림 순서 조정 self.index < 0 ? self.index = self.img.length - 1 : self.index; .색인 >= self.img.length ? self.index = 0 : self.index; return false; } self.data.forEach(function (item) { var pos = Math.tween.Cubic.easeInOut(t - self.startTime, 0, (self.imgHeight) * val, self.duration2); // 현재 이미지 좌표 업데이트 self.context.drawImage(self.img[self.index], item.x1, item.y1 + pos, self.wx, self.wy, item.x, item.y, self.area, self.area) // 다음 이미지의 좌표 업데이트 self.context.drawImage(self.img[ self.index + val], item.x1, item.y1 + pos - self.imgHeight * val, self.wx, self.wy, item.x, item.y, self.area, self.area); }); requestAnimationFrame(애니메이션) })() }
x축 전환도 마찬가지입니다. 이제 모든 기능이 거의 완성되었습니다.
위의 내용은 이 기사의 전체 내용입니다. 모든 분들의 학습에 도움이 되기를 바랍니다. 또한 모든 분들이 VeVb Wulin Network를 지지해 주시길 바랍니다.