年終活動h5動畫總結
css3 + react-id-swiper + react + redux + saga
採用postcss的Autoprefixer外掛,即可滿足大多數oppo,vivo手機的相容性問題。
1. 動畫相關主要內容:
- 流星
- 閃爍星星
- 字的晃動
- 人的移動(動畫 + 監聽動畫結束時間)
- 橋的鋪墊
- 開啟旅程按鈕縮放
- 旋轉(+ 相容)
- react-id-swiper
- swiper配合css3實現切換
2. css3動畫語法
- animation
animation-name
animation-duration
animation-timing-function 速度曲線
animation-delay
animation-iteration-count
animation-direction
play-state
fill-mode
複製程式碼
- transform
transform 屬性向元素應用 2D 或 3D 轉換。該屬性允許我們對元素進行旋轉、縮放、移動或傾斜。 其中 transform-origin (屬性改變被轉換元素的中心)。
- transition
三者區別: animation 動畫,關鍵幀,往復性。 transition 過渡, 屬性,觸發動作,一過性。 transform 變換, 複雜的變換引數。
3. 示例 github.com/hytStart/Re…
- 流星
改變位置
translate3d
,透明度opacity
和大小scale
。
流星尾巴採用偽元素元素:after
旋轉-45deg
(旋轉基點為左transform-origin: left;
);採用border
可以實現,靠近頭部越亮,靠近尾部越暗。
.star {
display: block;
width: 5px;
height: 5px;
border-radius: 50%;
background: #FFF;
top: 10px;
left: 200px;
position: relative;
animation: star-ani 6s infinite ease-out;
box-shadow: 0 0 5px 5px rgba(255, 255, 255, .3);
opacity: 1;
}
.star:after {
content: '';
display: block;
top: 0px;
left: 40%;
border: 0px solid #fff;
border-width: 0px 90px 2px 90px;
border-color: transparent transparent transparent rgba(255, 255, 255, .3);
transform: rotate(-45deg) translate3d(1px, 3px, 0);
box-shadow: 0 0 1px 0 rgba(255, 255, 255, .1);
transform-origin: left;
}
@keyframes star-ani {
0% {
opacity: 0;
transform: scale(0) rotate(0) translate3d(0, 0, 0);
}
50% {
opacity: 1;
transform: scale(1) rotate(0) translate3d(-100px, 100px, 0);
}
100% {
opacity: 0;
transform: scale(1) rotate(0) translate3d(-200px, 200px, 0);
}
}
複製程式碼
- 閃爍星星
改變透明度
.shine {
background: url('../../../../images/action/icon-star1.png') no-repeat center;
background-size: 100%;
width: 30px;
height: 40px;
position: absolute;
top: 90px;
left: 100px;
opacity: 0;
animation: opacity-change 0.5s ease-in-out infinite;
}
@keyframes opacity-change {
0% {
opacity: 0;
}
50% {
opacity: 1;
}
100% {
opacity: 0;
}
}
複製程式碼
- 字
分為兩段動畫,下降和上升。
translateY
改變即可。
.text-item-0 {
position: absolute;
width: 25px;
height: 75px;
top: 60px;
left: 100px;
background: url('../../../../images/action/S-start.png') no-repeat center;
background-size: 100%;
animation: letter-0 1.5s ease-in-out both, letter-0-1 2.0s ease-in-out 1.5s both;
}
@keyframes letter-0 {
0% {
transform: translateY(0)
}
50% {
transform: translateY(80px)
}
100% {
transform: translateY(0px)
}
}
@keyframes letter-0-1 {
0% {
opacity: 1;
}
100% {
top: -80px;
opacity: 0;
}
}
複製程式碼
- 人移動
切換dom,新增類控制移動和暫停,以及切換背景人物。監聽
animationend
事件。
jsx
{
peopleMove ?
<div
className={`${style.people_move} ${!pausedState && style.people_paused}`}
ref={start2 => { this.start2 = start2 }}
/>
:
<div className={style.people} />
}
css
.people {
width: 20px;
height: 64px;
position: absolute;
left: 10px;
top: 130px;
background: url('../../../../images/action/people.png') no-repeat center;
background-size: 100%;
opacity: 0;
animation: peopleUp 1s ease-in-out 0.5s both;
}
.people_move {
background: url('../../../../images/action/people_moveleft.gif');
background-size: 100% 100%;
background-position: center;
background-repeat: no-repeat;
width: 20px;
height: 64px;
position: absolute;
left: 10px;
top: 130px;
opacity: 1;
animation: PeopleMove 1.5s linear 0s both;
}
.people_paused {
width: 20px;
height: 64px;
background: url('../../../../images/action/people.png');
background-size: 100% 100%;
background-position: center;
background-repeat: no-repeat;
animation-play-state: paused;
}
@keyframes peopleUp {
0% {
opacity: 0;
}
100% {
opacity: 1;
}
}
@keyframes PeopleMove {
0% {
left: 10px;
top: 130px;
}
100% {
top: 20px;
left: 180px;
background-size: 100% 100%;
background-position: center;
background-repeat: no-repeat;
}
}
// 監聽動畫結束時機
componentDidUpdate() {
const { peopleMove } = this.state
if (peopleMove) {
this.start2.addEventListener('animationend', this.end)
this.start2.addEventListener('webkitAnimationEnd', this.end)
this.start2.addEventListener('mozAnimationEnd', this.end)
this.start2.addEventListener('oAnimationEnd', this.end)
}
}
複製程式碼
- 橋的出現
配合
background-size: cover;
屬性實現。
.brige {
width: 0px;
background: url('../../../../images/action/bridge.png');
background-size: cover;
background-repeat: no-repeat;
height: 100px;
position: absolute;
top: 40px;
left: 30px;
animation: BridgeFadeIn 3s linear both;
opacity: 0;
}
@keyframes BridgeFadeIn {
0% {
width: 0px;
opacity: 0;
}
100% {
width: 200px;
opacity: 1;
}
}
複製程式碼
- 漸變大(開始旅程)
利用
transform scale
2D 縮放轉換。
.icon-ciecle {
display: block;
position: absolute;
left: 100px;
top: 80px;
width: 30px;
height: 30px;
background: url('../../../../images/action/icon-light.png') no-repeat center;
background-size: 100%;
animation: warn 1.2s ease-in-out 0s infinite both;
}
@keyframes warn {
0% {
transform: scale(0.1);
opacity: 0.0;
}
25% {
transform: scale(0.2);
opacity: 0.3;
}
50% {
transform: scale(0.4);
opacity: 0.5;
}
75% {
transform: scale(0.6);
opacity: 0.7;
}
100% {
transform: scale(0.8);
opacity: 0.0;
}
}
複製程式碼
- 旋轉
ios的
animation-play-state: paused;
不起作用,且animation
動畫不可寫在新增類裡,必須寫在一個類裡。(測試中發現,這裡有疑問。)
.music_img {
width: 40px;
height: 40px;
display: block;
position: absolute;
left: 100px;
top: 80px;
animation: rotating 3s linear infinite;
animation-play-state: running;
}
.rotate-pause {
animation-play-state: paused;
}
@keyframes rotating {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}
複製程式碼
- react-id-swiper
Swiper的react版本,基本api都支援,具實驗目測相當於Swiper v4。
- 不一定通過api。也可以通過重寫css實現。
- 重寫css切換效果的話,會影響它本身的動畫效果,所以可通過新增不可滑動來控制。
1.
.swiper-wrapper {
transition-delay: 1.6s;
}
2.
noSwiping: true,
noSwipingClass: 'stop-swiping',
複製程式碼
- Swiper實現的迷宮切換
1.swiper的
effect
屬性與控制背景圖片的opacity
,利用時間差實現最終效果。包括迷宮的切換,揹包男的出現與消失,彈窗的出現與消失。
2.swiper屬性的activeIndex
,可以得到滑動到第幾頁,通過改變dom
或者改變類,控制第幾頁動畫的發生。
// 根據activeIndex拿到的頁數,控制該頁數state的改變,通過切換dom和新增類的方式,達到進場與退場的動畫效果。
// 新增類stop-swiping控制不可滑動,動畫完成後,才可繼續滑動下一頁。
const mazeStyle = classNames({
[styles['maze-1']]: true,
[styles['maze-out']]: firstMazeOut,
})
const peopleStyle = classNames({
[styles['people-1']]: true,
[styles['people-out']]: firstMazeOut,
})
const popCardStyle = classNames({
[styles['pop-card-container']]: true,
[styles['pop-card-out']]: firstMazeOut,
})
{
firstMazeIn ?
<div className={!isSlideFirst && "stop-swiping"}>
<div className={styles['maze-container']}>
<div className={mazeStyle} />
<div className={peopleStyle} />
</div>
<div className={popCardStyle}>
<ModalScene {...this.props} />
</div>
</div>
:
<div /> // 不新增會影響swiper自身頁數的判斷
}
複製程式碼
4. 動畫總結
別人寫的真牛X,自己只會
opacity
,translate
。
5. 待完善
- html2canvas
- 資源載入
- jsbridge使用與注意事項