1. 程式人生 > 其它 >html、css、js、react、vue 文字一行一行顯示出來

html、css、js、react、vue 文字一行一行顯示出來

技術標籤:小功能jsreacthtmljavascriptvue

前端時間在做 年報,就不難涉及到 年報 具有的幾大特性:
1、頁面滑動特效
2、文字一行一行出現特效
3、頁面內動畫
等等

這片文章主要展示一下 文字一行一行出現特效 程式碼(react 為例)

先看效果圖:
在這裡插入圖片描述

第一步:分析
想要使 文字一行一行顯示出來 無非就三種方式
1、使用css控制:底層一個完整頁面,上層一個只有背景圖的完整頁面,通過動畫漸漸向下移動上層div,使底層div資訊逐漸展示出來,以達到漸顯的效果,但視覺效果較差。
2、使用js控制:使用js逐步增加dom節點,並給dom節點新增動畫效果。(對於React,Vue這類框架層面應用,不建議直接使用JS控制 DOM 節點增刪)

3、js + css控制:初次頁面全部渲染出來,並給每一行增加 id 屬性,但Css樣式為 opacity: 0 透明,渲染完成後,通過js改變 css 動畫 樣式。(本文所採用方式)

第二部:佈局
1、想要使文字一行一行展示出來,我們期望的是得到渲染陣列,這樣才能對順序進行控制。不同段落之間我們也期望 從上往下的段落一行一行展示出來,所以我們期望得到一個二位陣列,like this:

paragraphArrays : [
    [
        <Text text="第一段第一行" />,
        <Text text="第一段第二行" />,
        <Text text="第一段第三行" />,
    ],
    [
        <Text text="第二段第一行" />,
        <Text text="第二段第二行" />,
        <Text text="第二段第三行" />,
    ],
]

2、不同頁面的文字承載container樣式可能不同,所以我們期望使用者可自定義樣式。
3、不同頁面段落之間的樣式可能不同,所以我們期望使用者可自定義樣式。
4、使用者可自定義行 id

這樣,動畫元件的 props 我們就定義好了

第三部:實現
1、渲染函式:

js:

import React, { useEffect } from 'react'
import classnames from 'classnames'
import styles from './AnimationText.module.scss'

/**
 * @description: 文欄位落一行一行出現動畫
 * @param props:
 *  paragraphArrays : [
    [
        <Text text="第一段第一行" />,
        <Text text="第一段第二行" />,
        <Text text="第一段第三行" />,
    ],
    [
        <Text text="第二段第一行" />,
        <Text text="第二段第二行" />,
        <Text text="第二段第三行" />,
    ],
]
* paragraphClassName: 段落className
* className: container className
* id: container id
 * @return {*}
 */
 
function AnimationText(props) {
    const { paragraphArrays, paragraphClassName, className, id } = props || {}

    return (
        <div className={classnames(styles.container, className)} id={id || 'animationText'} >
            {(paragraphArrays || []).map((item, index) => (
                <div className={classnames(styles.paragraph, paragraphClassName)}>
                    {(item || []).map((component, idx) => (
                        <div id={`${index}-${idx}-${id || 'animationText'}`} className={styles.nodeItem} >
                            {component}
                        </div>
                    ))}
                </div>
            ))}
        </div>
    )
}

export default AnimationText

css:

.paragraph {
    margin-bottom: 15px;
}

.nodeItem {
    opacity: 0;
}

2、獲取 container DOM 節點

// 用於儲存 存放動畫的 container
let element
// 等同於 componentDidMount
useEffect(() => {
        element = document.getElementById(id || 'animationText')
 }, [])

3、獲取到DOM節點後, 開始執行動畫

css 動畫定義:

// 透明度從0 到 1,動畫只執行一次
.nodeItemShow {
    visibility: visible;
    animation: scan 3s ease 0s 1;
}

@keyframes scan {
    0% {
        opacity: 0;
    }

    100% {
        opacity: 1;
    }
}

js:

// 獲取到 element 後,迴圈 paragraphArrays 陣列,使用count 計數,每隔 600 * count 毫秒改變當前count 行的 css 樣式
useEffect(() => {
        if (element) {
            let count = 0
            for (let i = 0; i < paragraphArrays.length; i += 1) {
                for (let j = 0; j < paragraphArrays[i].length; j += 1) {
                    count += 1
                    setTimeout(() => {
                        const ele = document.getElementById(`${i}-${j}-${id || 'animationText'}`)
                        ele.setAttribute('class', styles.nodeItemShow)
                    }, 600 * count)
                }
            }
        }
}, [element])

完整程式碼:

js:

import React, { useEffect } from 'react'
import classnames from 'classnames'
import styles from './AnimationText.module.scss'

/**
 * @description: 文欄位落一行一行出現動畫
 * @param props:
 *  paragraphArrays : [
    [
        <Text text="第一段第一行" />,
        <Text text="第一段第二行" />,
        <Text text="第一段第三行" />,
    ],
    [
        <Text text="第二段第一行" />,
        <Text text="第二段第二行" />,
        <Text text="第二段第三行" />,
    ],
]
* paragraphClassName: 段落className
* className: container className
* id: container id
 * @return {*}
 */

function AnimationText(props) {
    const { paragraphArrays, paragraphClassName, className, id } = props || {}
    let element
    useEffect(() => {
        element = document.getElementById(id || 'animationText')
    }, [])
    useEffect(() => {
        if (element) {
            let count = 0
            for (let i = 0; i < paragraphArrays.length; i += 1) {
                for (let j = 0; j < paragraphArrays[i].length; j += 1) {
                    count += 1
                    setTimeout(() => {
                        const ele = document.getElementById(`${i}-${j}-${id || 'animationText'}`)
                        ele.setAttribute('class', styles.nodeItemShow)
                    }, 600 * count)
                }
            }
        }
    }, [element])
    return (
        <div className={classnames(styles.container, className)} id={id || 'animationText'} >
            {(paragraphArrays || []).map((item, index) => (
                <div className={classnames(styles.paragraph, paragraphClassName)}>
                    {(item || []).map((component, idx) => (
                        <div id={`${index}-${idx}-${id || 'animationText'}`} className={styles.nodeItem} >
                            {component}
                        </div>
                    ))}
                </div>
            ))}
        </div>
    )
}

export default AnimationText

css:

.container {
    width: auto;
}

.paragraph {
    margin-bottom: 15px;
}

.nodeItem {
    opacity: 0;
}

.nodeItemShow {
    visibility: visible;
    animation: scan 3s ease 0s 1;
}

@keyframes scan {
    0% {
        opacity: 0;
    }

    100% {
        opacity: 1;
    }
}

一個簡單的 文字一行一行顯示出來 動畫元件就實現啦