本文主要介绍了古卷轴展开动画的实现,包括 jquery 版本和 react 版本,还实现了从中间展开的动画效果和从右向左展开的动画效果。
当然,还可以结合古诗词淡入淡出动画效果,实现古诗词淡入动画和古画卷展开动画的组合效果。
jquery 版本
在网上找了一个 jquery 版本的实现,具体 demo 可以查看下列网址:古卷轴平滑打开 jQuery 动画特效
动画实现代码如下所示,直接从 demo 中复制过来,直接使用即可。
1$(document).ready(function () {
2
3 $(".l-pic-index").animate({ left: "95px", top: "-4px" }, 1300);
4 $(".r-pic-index").animate({ right: "-23px", top: "-5px" }, 1450);
5 $(".l-bg-index").animate({ width: "433px", left: "73px" }, 1500);
6 $(".r-bg-index").animate(
7 { width: "433px", right: "-38px" },
8 1500,
9 function () {
10 $(".main-index").fadeIn(800);
11 }
12 );
13});
react 版本
上述是用 jquery 实现的版本,但是一般现在开发都用 vue 和 react 框架了,不使用 jquery 了。
我的项目是使用 react 开发的,因此使用 react 重构一下,在 react 项目中实现该功能,整体而言,调整的东西也不多。
1. 初始状态:两个卷轴
固定初始状态的位置:只显示两个卷轴,画卷和内容部分不显示
1function Page() {
2 return (
3 <div className="reel_content">
4 <div className="l-pic-index"></div>
5 <div className="r-pic-index"></div>
6 <div className="l-bg-index"></div>
7 <div className="r-bg-index"></div>
8 <div className="main-index">
9 <p className="intro-text">xxx</p>
10 </div>
11 </div>
12 );
13}
14export default Page;
1.reel_content {
2 position: relative;
3 z-index: 99;
4 width: 900px;
5 height: 460px;
6 padding: 40px 0;
7 background-color: #6f0b02;
8 box-sizing: border-box;
9 .l-pic-index {
10 position: absolute;
11 left: 400px;
12 top: 1px;
13 z-index: 2;
14 width: 50px;
15 height: 460px;
16 background: url("../img/j1.png") no-repeat right 0;
17 }
18 .r-pic-index {
19 position: absolute;
20 right: 400px;
21 top: 0;
22 z-index: 2;
23 width: 50px;
24 height: 460px;
25 background: url("../img/j4.png") no-repeat left 0;
26 }
27 .l-bg-index {
28 position: absolute;
29 top: -3px;
30 left: 430px;
31 z-index: 1;
32 width: 25px;
33 height: 459px;
34 background: url("../img/j2.png") right 0 no-repeat;
35 }
36 .r-bg-index {
37 position: absolute;
38 top: -4px;
39 right: 430px;
40 z-index: 1;
41 width: 25px;
42 height: 459px;
43 background: url("../img/j3.png") 0 0 no-repeat;
44 }
45
46 .main-index {
47 display: none;
48 overflow: hidden;
49 zoom: 1;
50 position: absolute;
51 z-index: 5;
52 width: 530px;
53 height: 280px;
54 left: 145px;
55 top: 90px;
56 color: #2e2e2e;
57 }
58}
初始状态效果如下所示:
2. 结束状态:全部展开
固定所有内容的位置,显示卷轴中部和内容部分
1.reel_content_finish {
2 .l-pic-index {
3 left: 95px !important;
4 top: -4px !important;
5 }
6 .r-pic-index {
7 right: -23px !important;
8 top: -5px !important;
9 }
10 .l-bg-index {
11 width: 433px !important;
12 left: 73px !important;
13 }
14 .r-bg-index {
15 width: 433px !important;
16 right: -38px !important;
17 }
18 .main-index {
19 display: block !important;
20 }
21}
结束状态效果如下所示:
3. 卷轴展开:中间展开
将 jquery 版本的动画效果代码,转换一下为 react 的,如下所示:
1function Page() {
2
3 const [isAnimating, setIsAnimating] = useState(false);
4
5 const [mainIndexStyle, setMainIndexStyle] = useState({ display: "none" });
6
7 const [lPicIndexStyle, setLPicIndexStyle] = useState({});
8
9 const [rPicIndexStyle, setRPicIndexStyle] = useState({});
10
11 const [lBgIndexStyle, setLBgIndexStyle] = useState({});
12
13 const [rBgIndexStyle, setRBgIndexStyle] = useState({});
14
15 const [mainIndexVisible, setMainIndexVisible] = useState(false);
16
17 useEffect(() => {
18
19 const intervalId = setInterval(() => {
20 setIsAnimating(true);
21 }, 3000);
22
23 return () => clearInterval(intervalId);
24 }, []);
25
26 useEffect(() => {
27
28 if (isAnimating) {
29 animateElements();
30 }
31 }, [isAnimating]);
32
33 useEffect(() => {
34
35 if (mainIndexVisible) {
36 setTimeout(() => {
37 setMainIndexStyle({ display: "block" });
38 }, 800);
39 }
40 }, [mainIndexVisible]);
41
42
43 const animateElements = () => {
44 const lPicIndexStyle = {
45 left: "95px",
46 top: "-4px",
47 transition: "left 1300ms, top 1300ms",
48 };
49 const rPicIndexStyle = {
50 right: "-23px",
51 top: "-5px",
52 transition: "right 1450ms, top 1450ms",
53 };
54 const lBgIndexStyle = {
55 width: "433px",
56 left: "73px",
57 transition: "width 1500ms, left 1500ms",
58 };
59 const rBgIndexStyle = {
60 width: "433px",
61 right: "-38px",
62 transition: "width 1500ms, right 1500ms",
63 };
64
65
66 setTimeout(() => {
67 setMainIndexVisible(true);
68
69
70 setTimeout(() => {
71 resetAnimations();
72 }, 3000);
73 }, 1500);
74
75
76 setLPicIndexStyle(lPicIndexStyle);
77 setRPicIndexStyle(rPicIndexStyle);
78 setLBgIndexStyle(lBgIndexStyle);
79 setRBgIndexStyle(rBgIndexStyle);
80 };
81
82
83 const resetAnimations = () => {
84 setMainIndexVisible(false);
85 setMainIndexStyle({ display: "none" });
86 setLPicIndexStyle({});
87 setRPicIndexStyle({});
88 setLBgIndexStyle({});
89 setRBgIndexStyle({});
90 setIsAnimating(false);
91 };
92
93 return (
94 <div className="reel_content">
95 <div className="l-pic-index" style={{ ...lPicIndexStyle }}></div>
96 <div className="r-pic-index" style={{ ...rPicIndexStyle }}></div>
97 <div className="l-bg-index" style={{ ...lBgIndexStyle }}></div>
98 <div className="r-bg-index" style={{ ...rBgIndexStyle }}></div>
99 <div className="main-index" style={{ ...mainIndexStyle }}>
100 <p className="intro-text">xxx</p>
101 </div>
102 </div>
103 );
104}
105
106export default memo(Page);
我的版本(右向左展开)
上述实现的实现的版本是:从中间向两边展开的。不满足项目需求,所以需要修改。
而且上述的实现代码太复杂、太繁琐了,需要简化一下,直接使用 CSS3 动画就行了。
我的项目需求为:卷轴滚动时,从右向左打开,并且同步出现诗句
前提条件:将图片 j2.png 和 j3.png 合并成一张完整的图片
精简的代码,如下所示:
1function Page() {
2
3 const [isAnimating, setIsAnimating] = useState(false);
4
5 const [reelClass, setReelClass] = useState("reel_wrap");
6
7 useEffect(() => {
8 const intervalId = setTimeout(() => {
9 setIsAnimating(true);
10 }, 3000);
11
12 return () => clearTimeout(intervalId);
13 }, []);
14
15 useEffect(() => {
16
17 if (isAnimating) {
18 setReelClass("reel_wrap reel_wrap_finish");
19
20
21
22
23
24
25 }
26 }, [isAnimating]);
27
28 return (
29 <div className={reelClass}>
30 <div className="reel_l"></div>
31 <div className="reel_r"></div>
32 <div className="reel_bg"></div>
33 </div>
34 );
35}
36
37export default memo(Page);
样式实现:
1$reelWidth: 48px;
2$reelHeight: 384px;
3$reelBgWidth: 864px;
4$time: 3000ms;
5
6.reel_wrap {
7 position: absolute;
8 z-index: 99;
9 width: $reelWidth * 2 + $reelBgWidth;
10 height: $reelHeight;
11 background-color: #6f0b02;
12 box-sizing: border-box;
13 overflow: hidden;
14 &_finish {
15
16 .reel_l {
17 left: 0 !important;
18 transition: left $time ease-in-out;
19 }
20 .reel_bg {
21 width: $reelBgWidth !important;
22 left: $reelWidth !important;
23 transition: width $time ease-in-out, left $time ease-in-out;
24 }
25 }
26 .reel_l {
27 position: absolute;
28 z-index: 2;
29 left: $reelBgWidth;
30 width: $reelWidth;
31 height: $reelHeight;
32 background: url("../img/j1.png") 0 0 / 100% 100% no-repeat;
33 }
34 .reel_r {
35 position: absolute;
36 z-index: 2;
37 right: 0;
38 width: $reelWidth;
39 height: $reelHeight;
40 background: url("../img/j4.png") 0 0 / 100% 100% no-repeat;
41 }
42 .reel_bg {
43 position: absolute;
44 z-index: 11;
45 left: $reelWidth + $reelBgWidth;
46 width: 0;
47 height: $reelHeight;
48 background: url("../img/j5.png") right 0 no-repeat;
49 }
50}
其他系列文章:
个人笔记记录 2021 ~ 2025