記一次h5動畫之旅

金鐘罩鐵布衫發表於2019-01-01

年終活動h5動畫總結

戳這裡----♂--github--

css3 + react-id-swiper + react + redux + saga
採用postcss的Autoprefixer外掛,即可滿足大多數oppo,vivo手機的相容性問題。

記一次h5動畫之旅

1. 動畫相關主要內容:

  1. 流星
  2. 閃爍星星
  3. 字的晃動
  4. 人的移動(動畫 + 監聽動畫結束時間)
  5. 橋的鋪墊
  6. 開啟旅程按鈕縮放
  7. 旋轉(+ 相容)
  8. react-id-swiper
  9. 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…

記一次h5動畫之旅

  • 流星

改變位置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);
    }
}
複製程式碼
  • 閃爍星星

改變透明度

記一次h5動畫之旅

.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改變即可。

記一次h5動畫之旅

.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事件。

記一次h5動畫之旅

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;屬性實現。

記一次h5動畫之旅

.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 scale2D 縮放轉換。

記一次h5動畫之旅

.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動畫不可寫在新增類裡,必須寫在一個類裡。(測試中發現,這裡有疑問。)

記一次h5動畫之旅

記一次h5動畫之旅

.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。

  1. 不一定通過api。也可以通過重寫css實現。
  2. 重寫css切換效果的話,會影響它本身的動畫效果,所以可通過新增不可滑動來控制。
1. 
.swiper-wrapper {
    transition-delay: 1.6s;
}
2. 
noSwiping: true,
noSwipingClass: 'stop-swiping',
複製程式碼
  • Swiper實現的迷宮切換

1.swiper的effect屬性與控制背景圖片的opacity,利用時間差實現最終效果。包括迷宮的切換,揹包男的出現與消失,彈窗的出現與消失。
2.swiper屬性的activeIndex,可以得到滑動到第幾頁,通過改變dom或者改變類,控制第幾頁動畫的發生。

記一次h5動畫之旅

// 根據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使用與注意事項

相關文章