preface

When browsing a certain website accidentally, I was deeply attracted by the round seeding effect of its home page. Through a browser debugging, I finally understood the implementation principle, and finally wrote a Demo by hand, the final effect is as follows :(source code at the end)

Plane effect:

The 3 d effect:

It cuts an image into several pieces, then animates the pieces one by one, and finally splices them together into a new image.

In retrospect of the front-end technology stack, it seems that there is no technology that can just crop an image and slice it up. We usually only use canvas to simulate the image clipping effect.

Through my debugging, it is found that the same clipping effect can be achieved in a simpler way than canvas.

Then, step by step, we uncover the implementation principle.

Effect of surface

Start with the plane effect, first of all to write a section of ordinary HTML rotation diagram, the structure is as follows:

<div class="main" id="el"> <div class="item"> <img src="./img/1.jpg" /> </div> <div class="item"> <img src="./img/2.jpg"  /> </div> <div class="item"> <img src="./img/3.jpg" /> </div> </div>Copy the code

El is the outer container that contains three images in rotation.

If we want to make an animation, we have to follow a step.

  • Generates or gets the object to animatedomThe element
  • For thedomElement adds an animation effect

Now slow down the animation of the flat effect and observe its characteristics carefully, as shown below:

This image slice will not be found in the original HTML code, which means that the elements to be animated will need to be generated and added to the page document for rendering.

If the image is set to be cut into 5 pieces, the structure of the image slice generated is roughly as follows:

<div class="main" id="el"> <div class="item"> <img src="./img/1.jpg" /> </div> <div class="item"> <img src="./img/2.jpg"  /> </div> <div class="item"> <img src="./img/3.jpg" /> </div> <! - do the animation dom elements -- -- > < div class = "hook" > < div > < img SRC = ". / img / 2. JPG "/ > < / div > < div > < img SRC =". / img / 2. JPG "/ > < / div > < div > < img src="./img/2.jpg" /></div> <div><img src="./img/2.jpg" /></div> <div><img src="./img/2.jpg" /></div> </div> </div>Copy the code

Generate an HTML fragment named hook through JS on the original page and set the positioning mode to Absolute.

There are five divs inside hook, showing the corresponding image blocks of 2.jpg respectively. The problem now is how do you get each of these five divs to scale to show a part of the image?

If you set the total width of the hook outer container to 1000px, the five divs of child elements will take up 200px on average. You can make the second div show only the second img cut by setting the following.

<div style="position:absolute; width:200px; Left: 200 px; overflow:hidden"> <img src="./img/2.jpg" style="position:absolute; width:500%; left:-200px"/> </div>Copy the code

Since the total width of the outer layer is 1000px, cut it into 5 pieces and each div takes 200px(200 needs to be calculated by JS in the actual code). Move 200px to the right to occupy the position of the second slice.

The img width and left values are important, and their specific values are calculated from the total width and the number of slices.

Set width to 500%, meaning the image is 1000px wide to match the outermost container, and then move 200px to the left. Since the parent div set Overflow: Hidden, imagine the effect coming out.

All the above procedures should be concatenated in JS to produce a string of HTML, similar to the following:

for(i=0; i<n; HTML += '<div style="position:absolute; width:${unit_width}px; top:-100%; left:${ i * unit_width}; overflow:hidden"> <img src="${src}" style="position:absolute; width:${n*100}%; left:${-i * unit_width}px"/> </div> ` }Copy the code

The five divs are spliced and placed in

and added to the page document rendering. The resulting DOM element is the element to animate next.

Because the five divs have absolute positioning and a top value of -100%, they are positioned at the top once rendered on the page.

The next step is to add transition: Top Linear 0.25s to each div in the loop above.

Once the five divs are rendered, we can dynamically set the top value of each div to 0 in js. An animation is triggered, and each div on the page slides slowly down from the top.

After all the animation process is complete, we remove the JS generated DOM of hook and set the item(the original DOM containing the static image) that should be displayed from hidden to displayed. The entire animation process is now complete.

The 3 d effect

Flat effects are relatively easy to implement, while 3D flip effects require an understanding of THE 3D properties of CSS.

3D Properties Review

RotateX: rotating around the X axis, associated with horizontal bar movement; rotateY: rotating around the Y axis, associated with pole dancing; rotateZ: rotating around the Z axis, associated with the old clock dial.

The above three attributes usually contact more, no longer repeat. Let’s focus on the 3D properties.

.container{ perspective: 1200px; perspective-origin: right center; .wrapper { transform-style: preserve-3d; transform:translateZ(-100px); }}Copy the code

The outer container container contains a child wrapper. The child is the element to animate in 3D, so it must set a property transform-style: preserve-3D.

Only if preserve-3D is set can an element appear in 3D.

The two attributes of translateZ are different from those of translateX and translateY. TranslateX moves left and right in the plane, while translateY moves up and down in the plane.

TranslateZ is a 3D attribute. Instead of moving up, down, left, and right in the plane, translateZ moves in or out perpendicular to the plane.

TranslateZ (-100px) means the element has been moved 100px into the screen. According to the rule of near big far small, the final visual effect of the element is that the whole is smaller. If it is a positive 100px, move it 100px out of the screen, visually enlarging the element.

TranslateZ only works when applied to an element that has preserve-3D enabled, and preserve-3D enabled all 3D attributes.

A block is a DOM element that needs to be 3d transformed, so it needs to be preserve-3D and Transform. The parent container acts as a stage, while the block acts as an actor for the stage.

Perspective represents the distance between the audience and the stage. It can be imagined that the smaller the perspective value is, the closer the audience is to the stage, and the scene on the stage will be more clearly seen.

For the real scene, the perspective corresponds to the distance of the DOM elements from the user’s eyes. The larger the distance, the farther away the wrapper will be, the smaller and less clear the inside. Conversely, the larger the perspective, the larger the Wrapper will be and the clearer the internal details will be.

Perspective-origin can be understood as whether users sit on the left, middle or right of the audience to watch the performance on the stage, and the effects will naturally be different depending on the position of vision.

Now back to the topic, continue to study the 3d rotation effect of the map. Watch the animation in detail by slowing it down, as shown below:

After looking at the image above, I quickly realized that the elements to be animated are not present in the page and need to be generated dynamically in JS.

The animation element is a cube that contains two motion effects. One moves to the left, the other flips forward. It’s easy to move to the left, but set it to absolute positioning and dynamic left. Flipping is done by setting RotateX(-90deg) (equivalent to flipping 90 degrees along the X-axis).

The cube animation is not difficult to achieve, the difficulty is how to generate such a cube.

Draw cube

In the current scenario, only four sides of the cube need to be drawn: top, front, left, and right. The top stores the next image to be rotated, the front stores the current image, and the left and right sides are filled with black background to make the image more three-dimensional. The HTML structure is as follows:

<div class="wrapper"> <div class="left"></div> <div class="right"></div> <div class="front"><img src="1.jpg"></div> <div  class="up"><img src="2.jpg"></div> </div>Copy the code

Set left,right,front, and up to absolute position, width and height to be full of parent elements, and left and top to 0.

Front is the front face, which is already displayed on the plane without any manipulation.

To position the center point at the upper left corner, render 90 degrees inward along the Y-axis to create the profile.

.left {
  transform-origin: 0% 0%;
  transform: rotateY(90deg);
  background-color: #333;
}
Copy the code

You have to move the entire width to the right and then rotate it 90 degrees along the y axis to form the right side.

html += ` ... <div class="right" style="transform: translateX(${unit_width}px) rotateY(90deg);" >... </div> ... `Copy the code

The center point of the up surface is first set at the lower left corner, and then the top is formed by rotating 90 degrees around the X-axis and moving up the whole height.

.up{
    transform-origin: 0% 100%;
}
Copy the code
html += ` ... <div class="up" style="transform: rotateX(${container_height}px);" >... </div> ... `Copy the code

The four-sided DOM structure is concatenated in JS and placed inside the parent div of the Wrapper. The wrapper element is the DOM element that does the animation.

The Wrapper element is then thrown into the stage element Container, which sets the 3D properties Perspective and Perspective-Origin.

A Container corresponds to a cube slice, and all the cut cubes are stitched together into HTML strings that are rendered in the page document. This generates the DOM element for the animation.

Add flip animation

The dom element to perform the animation has been generated and rendered on the page. As per our previous analysis, we can now flip the cube by adding a rotateX(-90deg) property to each cube (the wrapper div).

The Wrapper with the rotateX(-90DEg) property does indeed flip forward, but does not end up near the ground.

The reason for the accident is that the Wrapper is a cube, not a simple plane, it contains four planes. Now if I rotate the cube, it’s very important where the center of the cube is.

The wrapper height is 100% full of the entire parent. Through the test, it is found that the cube is actually based on the front (front) as the datum. The center of the cube is located in the middle of the front height. If the front is 600px, draw an X-axis at 300px and rotate the cube along that axis. If the front is 800px high, then the cube rotates along a 400px axis.

Now going back to the example above, the front is set to be 100% full of the parent element, and the cube is rotated 90 degrees forward with an axis in the middle, so there is a hollow underneath. We can’t flip the cube over and stop it in midair, we have to find a way to stick it back to the ground.

As mentioned before, the cube is based on the front side, and when it is rotated 90 degrees, the front side comes to the bottom. To move the cube down because the front is facing the bottom, just set translateZ(contrainer_height/2) to move the cube half way down to the bottom.

This ensures that the flipped cube is stuck to the bottom, but the images inside it are distorted.

Because when the cube is rotated forward with rotateX(-90deg), it is actually moved forward, according to the rule of near larger and far smaller, the visual effect of the image is magnified. To solve this problem, first push the cube half the height in with translateZ(-contrainer_height/2) and then flip the cube to the front.

While ((el = eles.shift())) {// The cube is pushed back half height along the Z-axis, then rotated forward, then rotated down half height to the bottom el.style.transform = `translateZ(${ -this.container_height / 2 }px) rotateX(-90deg) translateZ(${this.container_height / 2}px)`; . }Copy the code

After the flipping effect is finished, we will find that the cube is being flipped right in front of us, and we can’t see the right side of the cube with the black background, which will show no three-dimensional feeling.

One attribute, perspective-Origin: Right Center, was introduced earlier. It’s as if the audience is sitting on the right side of the auditorium, and the stage is as wide as the auditorium, and there’s an iron cage on top of the stage that’s facing the front of the audience. At this point, the audience can only see the front of the cage. If the cage is pushed to the left of the stage, the customer sitting on the right of the audience can see not only the front of the cage, but also the right of the cage.

Similarly, in order to make the cube look more three-dimensional when it is flipped, the black background on the right side can be displayed during the animation. To do this, you need to move the cube to the left, and with the support of the Perspective-Origin property, you have the 3d effect of flipping the cube.

If you want the cube to move to the left, you simply get the cube’s DOM element and dynamically assign its left value.

The source code

The complete code