Previously, when I was browsing CSS design awards, I found an effect of dividing the content of the picture (web address: https://weareludwig.com You can click in and have a look. It feels dazzling and cool. So you try to achieve it yourself. The effect is good. Effect View https://codepen.io/geeknoble/....
Analysis
First, we can find that the content of the picture is divided into a small rectangle, and each rectangle is randomly translated. The drawImage function of Canvas can cut the content of the picture and draw it into Canvas canvas, so the main principle of this effect is to use drawImage. There are two main effects, one is the disruption and restoration of image content, the other is the switching between the next image and the next one. Both effects can use drawImage, but the distance of movement is different. With the general idea, we can start to realize it.
Initial work
First, we initialize some variables, such as the width and height of the picture, the number of rectangles, the size of the cut, etc. Then we calculate the coordinates of each rectangle, and use a double loop to save the rectangular coordinates in data. Each rectangle has a random displacement, which also needs to be preserved in random. Where x,y denotes the coordinates of canvas canvas canvas, x1,y1 denotes the coordinates of image clipping.
init: function (context, width, height, area, img) { this.context = context; this.img = img; this.imgWidth = img[0].width; //Picture width and height this.imgHeight = img[0].height; this.index = 0; //Current Picture Number this.width = width; //Canvas width and height this.height = height; this.area = height/12; //Small rectangular length this.countX = width / this.area; //Small Rectangles in Horizontal and Vertical Directions this.countY = height / this.area; this.wx = this.imgWidth / this.countX; //The width and height of the picture in a small rectangle this.wy = this.imgHeight / this.countY; this.state = true; //Picture status, true means unclassified this.dataFlag = true; //Small rectangular coordinate states, true denotes the absence of random values this.duration = 1000; //Animation time this.duration2 = 1500; this.startTime = 0; this.data = []; //Small rectangular coordinate information this.randoms = []; //Random value of position //Initialize rectangular coordinates var 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); //Store rectangular coordinates this.data.push({ x1: x1, y1: y1, x: x, y: y }); //Adding Random Values this.randoms.push(random(-this.area, this.area)); x1 += this.wx; x += this.area; } x1 = 0; y1 += this.wy; x = 0; y += this.area; } this.checkMargin(); }
Edge Detection
Before adding displacement to the rectangle, we need to determine whether the coordinates after displacement exceed the picture boundary. For example, if the rectangle at the top moves on the Y axis, it can only move upward, if the current coordinate plus displacement value is less than 0 or larger than the width of the picture. If the updated coordinates are less than 0, then the random value must be negative. We need to change the random value to positive number. If the coordinates are higher than the height of the picture, then we can change it to negative number. Since every rectangle moves in one direction, I write here as an even displacement moving x-axis and an odd displacement moving y-axis.
//Edge Detection checkMargin: function () { var self = this; this.data.forEach(function (item, index) { if (index % 2 == 0) { // Move the x-axis when the subscript is a multiple of 2, otherwise move the y-axis if ( item.x1 + self.randoms[index] < 0) // Change to positive self.randoms[index] = -self.randoms[index]; if (item.x1 + self.wx + self.randoms[index] > self.imgWidth ) // Change to negative number 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]) } }) }
Separation and recovery
The separation and restoration of animation content is to update the value of rectangular coordinates. If the content is disturbed, only the coordinates in the data are added with random values, and the restoration is to subtract the random values.
//Adjust rectangular coordinates update: function (val) { var self = this; if (val) { //Reduction coordinates if (!this.dataFlag) { this.data.forEach(function (item, index) { if (index % 2 == 0) { item.x1 -= self.randoms[index]; } else { item.y1 -= self.randoms[index]; } }) } this.dataFlag = true; } else { //Disturbing coordinates if (this.dataFlag) { this.data.forEach(function (item, index) { if (index % 2 == 0) { item.x1 += self.randoms[index]; } else { item.y1 += self.randoms[index]; } }) } this.dataFlag = false; } }
After storing the coordinates, we can realize the translation drawing. The movement process has a smooth transition. We can use the Tween.js slow motion algorithm. The algorithm has four parameters: the current time, the initial position, the end position and the animation time. Details can be found in this article by Zhang Xinxu. https://www.zhangxinxu.com/wo... . Through Tween.js, you can calculate the distance of each frame to move, and then use the request Animation Frame to update the coordinates.
blockAnimation: function () { var flag = 1; if (this.state) { // Judge whether to scramble or restore the picture this.update(true) } else { flag = -1; this.update(false); } var self = this; this.startTime = +new Date(); // Get the current time this.state = !this.state; (function animation() { var t = +new Date(); if (t >= self.startTime + self.duration) { // Animation End Conditions return false; } self.data.forEach(function (item, index) { if (index % 2 == 0) { var pos = Math.tween.Expo.easeInOut(t - self.startTime, 0, self.randoms[index] * flag, self.duration); // Calculate the moving distance of each frame 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] * flag, 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(animation); })(); }
So far, the animation of separation and recovery has been realized.
Picture switching
Next, we begin to deal with the image switching part, which is a bit like the rotation map. The rotation map animation is to move the distance of the width of the visual window for each picture position. The same is true here. The switching on the y axis can be achieved by adding the coordinates and the height of the picture. Unlike the rotation chart, we only have one canvas tag. When switching, we only need to change the coordinates of the current graph and the next graph. The current graph has a moving distance of y1 + pos, and the next graph has a moving distance of y1 + pos - imgHeight.
//Vertical Sliding Animation verticalAnimation: function (val) { if (!this.time2) { return false; } this.checkTime(2); var self = this; val ? val = 1 : val = -1; //Judging whether to go up or down if ((this.index + val) < 0 || (this.index + val) >= (this.img.length)) { //Judging whether the serial number of the picture is in the end return false; } this.state ? 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--; //Adjust the order of pictures self.index < 0 ? self.index = self.img.length - 1 : self.index; 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); // Update current picture coordinates self.context.drawImage(self.img[self.index], item.x1, item.y1 + pos, self.wx, self.wy, item.x, item.y, self.area, self.area); // Update the coordinates of the next picture 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(animation); })() }
The switch of the x-axis is the same. Now all the functions are almost complete. The complete code can be viewed in codepen.