Preface
When I visited a certain website unintentionally, I was deeply attracted by the carousel effect on its homepage. Through the browser debugging, I finally understood the implementation principle, and finally wrote a copy by myself Demo
. The final effect is as follows:( (The source code is at the end)
Plane effect:
3D effect:
This carousel cuts the picture into several pieces, then plays the cut pieces with animation effects in turn, and finally stitches them into a new picture.
In the process of recalling the front-end technology stack, it seems that there is no technology that can directly crop the image and divide it into several pieces. The image cropping effect we usually apply is only used canvas
to simulate the generation.
Through my own debugging, it turns out that this carousel picture uses a canvas
simpler way to achieve the same cropping effect.
Next, proceed from the shallower to the deeper, step by step to reveal the implementation principle.
Plane effect
Let's start with the plane effect, first write a general html
carousel 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>
el
It is the outer container, which contains three carousel pictures.
If we want to make an animation effect, we have to follow a step.
- Generate or get the
dom
elements to be animated dom
Add animation effects to the element
Now slow down the animation of the plane effect and carefully observe its characteristics, as shown in the following figure:
html
This picture slice is not found in the original code, which means that the elements to be animated need to be generated by us and added to the page document for rendering.
If the setting picture will be cut into 5
parts, then we generate a slice image 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>
<!--做动画的dom元素-->
<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>
In the original page by js
generating a class called a period hook
of html片段
, as a positioning mode absolute
.
hook
There are five div
, respectively, show 2.jpg
the corresponding picture cut problem now facing is how to let this 5
one div
were to scale this picture a part of it?
If setting hook
the outer container has a total width of 1000px
the sub-element 5
number div
average of 200px
may be provided by a second let div
only show img
the second cuts.
<div style="position:absolute;width:200px;left:200px;overflow:hidden">
<img src="./img/2.jpg" style="position:absolute;width:500%;left:-200px"/>
</div>
Since the total width of the outer layer 1000px
, cut into 5
parts each div
representing 200px
(in actual coding 200
through js
calculated), and then moved to the right 200px
occupying the second slice position.
img
The assignment of the width
sum left
is the key point, and its specific value is also calculated by the total width and the number of slices.
width
Set to 500%
, which means that the width of the picture is equal to 1000px
the outermost container, and then move to the left 200px
. Because the parent is div
set overflow:hidden
, imagine the effect and it will come out.
These processes mentioned above should be js
spliced and calculated in a string html字符串
, similar to the following:
for(i=0;i<n;i++){ //循环i次,n(总共将图片切成n份),unit_width对应每一个切块的宽度
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>
`
}
5
After the div
stitching is completed, put it into <div class="hook"></div>
it and add it to the page document rendering. The final generated dom
element is the element to be animated next.
Since 5
a div
set of absolute positioning and top
value is set -100%
, so they once rendered is in the top position on the page.
The next animation effect is very easy to achieve, we only need to div
add one to each in the above loop transition:top linear 0.25s
.
Once that 5
a div
rendering is complete, we can js
dynamically set in each div
of the top
value of 0
animation is triggered, each page div
slowly sliding down from the top.
Wait until all the animation process is completed, we will hook
this use js
generated dom
removed off, and that should be displayed item
(containing static pictures of the original dom
) is set to display hidden from this point the entire animation process is complete.
3D effect
The planar effect is relatively simple to implement, and the 3D
flipping effect requires an understanding of css
the 3d
attributes first.
3d property review
rotateX
: Around the X
axis of rotation, Lenovo 单杠运动
.
rotateY
: Around the Y
axis of rotation, Lenovo 钢管舞
.
rotateZ
: Around the Z
axis of rotation, Lenovo 老式钟表盘
.
The above three attributes are usually contacted a lot, so I won't repeat them. The following will focus on the 3d
attributes.
.container{
perspective: 1200px;
perspective-origin: right center;
.wrapper {
transform-style: preserve-3d;
transform:translateZ(-100px);
}
}
The outer container container
contains a child wrapper
. The child is the specific 3d
element to be animated, so it must set a property transform-style: preserve-3d
.
Only when it is set can the preserve-3d
element show its 3d
effect.
translateZ
And translateX
, translateY
these two attributes are not the same. translateX
Is about to move in the plane, translateY
is moved in the vertical plane.
translateZ
It is an 3d
attribute.It does not make up, down, left, and right displacements on the plane, but makes displacements in or out of the direction perpendicular to the plane.
translateZ(-100px)
It means that the element has moved to the inside of the screen 100px
. According to the rule of near, big, far, small, the final visual effect of the element is 100px
reduced overall. If it is positive , it will move to the outside of the screen 100px
, and the visual effect of the element is enlarged.
translateZ
Only preserve-3d
apply on the element with the attribute set will have an effect, so that preserve-3d
all 3d
attributes can take effect.
block
It is 3d
the dom
element to be transformed , so it needs to be set preserve-3d
and transform
transformed. The parent element is container
equivalent to a stage and block
can be regarded as an actor of stage performance.
perspective
It represents the distance between the audience and the stage.It is conceivable perspective
that the smaller the value, the closer the audience is to the stage, and the scene on the stage will be seen more clearly.
Corresponding to the real scene, perspective
corresponding to dom
the distance of the elements in the user's eyes, the larger the distance, the farther away, the wrapper
smaller the interior and the less clear. On the contrary, the perspective
larger the wrapper
display, the larger the appearance and the clearer the internal details. .
perspective-origin
It can be understood as whether the user is sitting in the left position, the middle position or the right position of the auditorium to watch the performance on the stage, and the effect of the visual field is different.
Now back to the topic, continue to study 3d
the carousel diagram of the flip effect. By slowing down the animation time, observe its details, as shown in the following figure:
By observing the above picture, I quickly realized that the elements to be animated are not available on the current page and need to be js
dynamically generated by us.
The animation element is a cube, which contains two motion effects. One is to move to the left and the other is to flip forward. Moving to the left is easy to do, set it to an absolute positioning dynamic change left
value. And flipping can RotateX(-90deg)
be done by setting Arrived (equivalent to X
flip 90
degree along the axis ).
The animation effect of the cube is not difficult to achieve, the difficulty lies in how to generate such a cube.
Draw cube
In the current scene, the cube only needs to draw four faces: top, front, left and right. The top is stored for the next picture to be carousel, the front is stored for the currently displayed picture, and the left and right are black. The background is filled to make the picture 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>
The left
, right
, front
and up
are set to absolute positioning, width and height are filled with the parent element, left
and top
setting 0
.
front
It is the face directly in front, it is originally displayed on the plane, and there is no need to do any processing.
left
To position the center point in the upper left corner, the y
rendering 90
degree along the axis will form the side.
.left {
transform-origin: 0% 0%;
transform: rotateY(90deg);
background-color: #333;
}
right
You need to move the entire width to the right and then rotate along the y axis 90
to form the right side.
html += `
... //js中dom字符串拼接
<div class="right" style="transform: translateX(${unit_width}px) rotateY(90deg);">
...
</div>
...
`
up
The surface first sets the center point at the lower left corner, rotates around the X
axis 90
and then moves up the entire height to form the top.
.up{
transform-origin: 0% 100%;
}
html += `
... //js中dom字符串拼接
<div class="up" style="transform: rotateX(90deg) translateZ(${container_height}px);">
...
</div>
...
`
The dom
structure of these four faces js
is spliced inside and put into the wrapper
corresponding parent div
. As mentioned above , the attributes wrapper
need to be set inside preserve-3d
, and the wrapper
elements are the elements that are really animated dom
.
wrapper
After the element is encapsulated, it is thrown into the stage element container
, and the attributes and container
are set .3d
perspective
perspective-origin
One container
corresponds to a cube slice, and the html
string formed by all the cut cubes is put into the page document and rendered. In this way, the animated dom
elements are generated.
Add flip animation
The dom
elements that perform the animation have been generated and rendered on the page. According to the previous analysis, adding an attribute to each cube ( wrapper
corresponding div
) rotateX(-90deg)
can make the cube flip. The results are as follows:
wrapper
In addition, the rotateX(-90deg)
attribute is indeed flipped forward, but the final position is not close to the ground.
The cause of the accident was because it wrapper
was a cube, not a simple plane, it also contained four planes. Now when the cube is rotated, the position of its center point is very important.
wrapper
The height of is 100%
full of the entire parent. Through testing, it is found that the attribute transformation of the cube is actually based on the front ( front
face). The center point of the cube is located in front
the middle of the front
height of the face . If the height of the face is 600px
, then When 300px
drawing an X
axis, rotate the entire cube along this axis. If the front
height of the surface is 800px
, then the cube will 400px
rotate along the axis.
Now go back to the above case, front
the height of the face is set to 100%
fill the parent element, then the cube will draw an axis in the middle and flip forward 90
, so there will be a hollow below. We can't let the cube turn and stop in mid-air , To find a way to put it back on the ground.
According to what I said before, the cube uses the front
surface as the reference surface. After flipping the 90
degree, the front
surface comes to the bottom. Then you want the cube to move down, because it is front
facing the bottom at this time , and you only need to set it translateZ(contrainer_height/2)
to move the cube down. Half the height is attached to the bottom.
Although this setting can ensure that the flipped cube is attached to the bottom, the picture inside it is deformed.
Because when the cube is set rotateX(-90deg)
to flip forward, it is actually moved forward. According to the rules of near large and far small, the visual effect of the picture is enlarged. In order to solve this problem, first use the cube translateZ(-contrainer_height/2)
to push half of the height inside and then let the cube Flip forward, so that the problem of image stretching is solved, the code is as follows:
while ((el = eles.shift())) {
//el对应着每个立方体的dom
//立方体先沿Z轴往后推一半高度,再朝前做翻转,翻转完后向下移动一半高度贴底
el.style.transform = `translateZ(${
-this.container_height / 2
}px) rotateX(-90deg) translateZ(${this.container_height / 2}px)`;
...
}
After the flip effect is finished, we will find that the cube is facing us and flipping, we can't see the right side of the cube with a black background right
, so it will appear very non-dimensional.
I introduced an attribute earlier perspective-origin:right center
. This is like the audience sitting on the right side of the auditorium, and the stage is as wide as the auditorium, and there is an iron cage on the stage facing the front of the audience. At this time, the audience's field of vision can only see On the front of the iron cage, if the cage is pushed to the left side of the stage, then the customer sitting on the right side of the audience can not only see the front of the iron cage, but he can also see the right side of the iron cage.
In the same way, in order to make the cube appear more three-dimensional when it is flipped, the black background on the right can be displayed during the animation. In order to achieve this purpose, you need to move the cube to the left, plus perspective-origin
the support of attributes, the cube The flipped three-dimensional effect comes out.
If you want the cube to move to the left, you only need to get the dom
elements of the cube and left
assign them dynamically.