React + Vite 实现一个音乐网站(动画篇)

为了让网站能够炫酷一点,必然的动画是不可或缺的

现在实现一个类似canvas流动背景的功能,最初设计是遍历多个小球在页面上,然后小球在dom节点内移动变换。后决定加入小球一起变换,让小球跟随大球移动,同时小球也能有自己的运动轨迹
知识点dom渲染以及TweenMax

1.代码实现

根目录建立文件夹pages,内部建立Home文件夹,Home内建立index.jsx以及index.scss其中Home将作为我们的起始页面,这在router中可以配置根路径’/’。
在这里插入图片描述

index.jsx代码

//引入css
import './index.scss'

function Home(){
	return (
		<div>
			//放入页面元素
		</div>
	)
}

export default Home

index.scss由于是模块化开发,这部分代码没有

既然我们建立了这个页面,现在我们需要把我们的swiper部分渲染到页面上
在component里面建立SwipperBanner文件夹,内部照例建立index.jsx以及index.scss文件
在这里插入图片描述

index.jsx代码

import './index.scss'

function SwiperBanner(params) {
	const bigCirleRef=useRef(null)
    const smallCirleRef=useRef(null)
	return(
        <div className='Allpart'>
            <div className='bigimg'>
                <img className='widthFixImg' src="../image/banner-img.png" alt="" />
                <div className='allcircle'>
                    <div className='bigcircleList circle' ref={bigCirleRef}>
                        {/* <div className='bigcircle'></div> */}
                    </div>
                    <div className='smallcircleList circle' ref={smallCirleRef}>

                    </div>
                </div>
                
                <div className='center'>
                    <div className='center-img-box'>
                        <img className='center-text-img' src="../image/musicText.png" alt="" />
                        
                    </div>
                    <div className='text'>
                        Share & Music Online & Love Dj
                    </div>
                    <div className='btn'>
                        LET LISTEN NOW
                    </div>
                </div>
            </div>
        </div>
    )
}

export default SwiperBanner

然后给我们的文件加入配置好的样式(index.scss)

.Allpart{
    .bigimg{
        position: relative;
        z-index: -1;
        .widthFixImg{
            width: 100%;
            height: auto;
            object-fit: cover;
        }
        .allcircle{
            position: absolute;
            // z-index: 3;
            top: 4.7rem;
            width: 100%;
            height: calc(100% - 4.7rem);
            overflow: hidden;
            .circle{
                position: absolute;
                top: 0;
                // left: 30rem;
                .bigcircle{
                    // width: 15rem;
                    // height: 15rem;
                    border-radius: 50%;
                    // background-color: yellowgreen;
                    opacity: 0.5;
                    transition: none linear 3s;
                    transition-property: left,top,background-color;
                }
                .smallcircle{
                    // width: 15rem;
                    // height: 15rem;
                    border-radius: 50%;
                    // background-color: yellowgreen;
                    opacity: 0.5;
                    transition: none linear 3s;
                    transition-property: left,top,background-color;
                }
                
            }
        }
        
        .center{
            position: absolute;
            top: 50%;
            left: 50%;
            transform: translate(-50%,-50%);
            .center-img-box{
                // position: relative;
                .center-text-img{
                    width: 30rem;
                }
                
            }
            
            .text{
                color: white;
                font-size: 1rem;
                font-family: 'Franklin Gothic Medium', 'Arial Narrow', Arial, sans-serif;
                letter-spacing: 0.2rem;
                text-align: center;
                margin-top: 0.5rem;
            }
            .btn{
                font-size: 0.7rem;
                width: 8rem;
                height: 1.6rem;
                line-height: 1.6rem;
                color: #f8f9fc;
                background: linear-gradient(to right ,#d62eb2 ,#126fc6);
                border-radius: 1rem;
                text-align: center;
                margin:1rem auto;
            }
        }
    }
}

这里的代码思路呢是将大球通过js代码渲染到bigCirleRef节点,小球渲染到smallRef节点
所以我们需要写一下js代码(index.jsx)

import './index.scss'
//模块引入
import {useEffect,useRef} from 'react'


function SwiperBanner(params) {
	//获取dom节点
	const bigCirleRef=useRef(null)
    const smallCirleRef=useRef(null)
	
	//储存能移动的面积(x,y)
	let aearMsg={}
	//这里是全局定义的定时器
    let interGetheight=''


	//首先获取到dom的信息,包括dom的高度和宽度也就是我大小球能运动的范围
	const getDomMsg=(dom)=>{
        return new Promise((resolve,reject)=>{
            console.log("clientWidth",dom.clientWidth);
            console.log("clientHeight",dom.clientHeight);
            aearMsg.canMoveX=dom.clientWidth;
            aearMsg.canMoveY=dom.clientHeight
            interGetheight=setInterval(() => {
                console.log("clientHeight",dom.clientHeight);
                if (dom.clientHeight!=0) {
                	//获取到dom高度后对定时器进行移除
                    clearInterval(interGetheight)
                    //轨迹大小
                    resolve({
                        canMoveX:dom.clientWidth,
                        canMoveY:dom.clientHeight
                    })
                }
            }, 1000);
        })
    }
	//定义render函数将小大球渲染到页面
	const render=(dom)=>{
        console.log("ranmgeee");
        getDomMsg(dom).then(res=>{
            let {canMoveX,canMoveY}=res
           	//创建大球的函数 
           	bigcircleRender(3,bigCirleRef.current,'15rem','15rem',canMoveX,canMoveY)
            //创建小球的函数 
            smallcircleRender(3,smallCirleRef.current,'6rem','6rem',canMoveX,canMoveY)
            smallcircleRender(2,smallCirleRef.current,'4rem','4rem',canMoveX,canMoveY)
        })
    }

	//我们动态产生小球的函数num为数量,renderDom为我们需要渲染到的dom节点,width以及height为小球大小,canMoveX以及canMoveY为能移动的范围
	const smallcircleRender=(num,renderDom,width,height,canMoveX,canMoveY)=>{
        const cirlceNumber=num
        let _fragment=document.createElement('div')
        for (let index = 0; index < cirlceNumber; index++) {
            let top=Math.floor(Math.random()*canMoveY+1)-150
            let left=Math.floor(Math.random()*canMoveX+1)-150
            if (top<0) {
                top=0
            }
            if(left<0){
                left=0
            }
            console.log(top,left);
            let _dom= document.createElement('div')
            _dom.setAttribute('class','smallcircle circle')
            _dom.setAttribute('style',`top:${top}px;left:${left}px;background-color:${colorRandom()};width:${width};height:${height}`)
            _dom.style.width=width
            _dom.style.height=height
            _fragment.appendChild(_dom)
        }
        // renderDom.innerHTML=_html
        renderDom.appendChild(_fragment)

        document.querySelectorAll('.smallcircle').forEach((domItem,index)=>{
            new TimelineMax({
                repeat:-1,
                yoyo:true,
            }).to(domItem,Math.random()+0.4,{
                width:domItem.clientWidth*1.25,
                height:domItem.clientWidth*1.25,
                
            })
        })
        domMove(document.querySelectorAll('.smallcircle'),canMoveX,canMoveY,width,height)
    }

	//创建大球同理,至于为什么分开成两个函数,是因为想做小球会跟随大球的动画,写在一个函数不便于解耦
	const bigcircleRender=(num,renderDom,width,height,canMoveX,canMoveY)=>{
        const cirlceNumber=num
        let _fragment=document.createElement('div')
        for (let index = 0; index < cirlceNumber; index++) {
            let top=Math.floor(Math.random()*canMoveY+1)-150
            let left=Math.floor(Math.random()*canMoveY+1)-150
            if (top<0) {
                top=0
            }
            if(left<0){
                left=0
            }
            let _dom= document.createElement('div')
            _dom.setAttribute('class','bigcircle circle')
            _dom.setAttribute('style',`top:${top}px;left:${left}px;background-color:${colorRandom()};width:${width};height:${height}`)
            _dom.style.width=width
            _dom.style.height=height
            _fragment.appendChild(_dom)
        }
        // renderDom.innerHTML=_html
        renderDom.appendChild(_fragment)
        document.querySelectorAll('.bigcircle').forEach((domItem,index)=>{
            new TimelineMax({
                repeat:-1,
                yoyo:true,
            }).to(domItem,Math.random()+0.4,{
                width:domItem.clientWidth*1.25,
                height:domItem.clientWidth*1.25,
                ease: Circ.easeOut,
                glowFilter:'#fff'
            })
        })

        
        
        domMove(document.querySelectorAll('.bigcircle'),canMoveX,canMoveY,width,height)
        
    }
	
	//上述运用到了colorRandom函数来实现随机颜色
	const colorRandom=()=>{
        var r = Math.floor(Math.random() * 256); //随机生成256以内r值
        var g = Math.floor(Math.random() * 256); //随机生成256以内g值
        var b = Math.floor(Math.random() * 256); //随机生成256以内b值
        return `rgb(${r},${g},${b})`; //返回rgb(r,g,b)格式颜色
    }
	
	//需定义domMove函数来做小球的运动动画
	const domMove=(dom,areaX,areaY,width,height)=>{
        let doChange=()=>{
        	//遍历我们的球
            dom.forEach((item,index)=>{
                let top=Math.floor(Math.random()*(areaY-item.clientHeight)+1)
                let left=Math.floor(Math.random()*(areaX-item.clientWidth)+1)
                if (top<0) {
                    top=0
                }
                if(left<0){
                    left=0
                }
                // console.log("zhix",top,left);
                item.style.top=top+'px'
                item.style.left=left+'px'
                item.style.backgroundColor=colorRandom()
                
            })
            
        }
        //
        doChange()
        setInterval(() => {
        	//小球跟随动画
            smallBarFollow()
        }, 1000);
        setInterval(() => {
            doChange()
        }, 3000);
    }

	//定义小球跟随动画
	const smallBarFollow = () => {
        document.querySelectorAll('.smallcircle').forEach((smallItem, index)=>{
            document.querySelectorAll('.bigcircle').forEach((bigItem, index)=>{
            	//条件判断小球周围是否有大球
                if ((bigItem.offsetTop-smallItem.offsetTop < 25 && bigItem.offsetTop - smallItem.offsetTop > -25) && (bigItem.offsetLeft - smallItem.offsetLeft < 25 && bigItem.offsetLeft - smallItem.offsetLeft < -25)) {
                    let smallBarTop = Math.floor(Math.random() * bigItem.style.top.replace(/px/g,'') + 1) + 'px',
                        smallBarLeft=Math.floor(Math.random() * bigItem.style.left.replace(/px/g,'') + 1) + 'px'
                    smallItem.style.top = smallBarTop
                    smallItem.style.left = smallBarLeft
                    // console.log("小球跟着大球走",smallBarLeft,smallBarTop);
                }
            })
        })
    }

	//使用useEffect函数
	useEffect(()=>{
        render(document.querySelector('.allcircle'))
    },[])
	
	return(
        <div className='Allpart'>
            <div className='bigimg'>
                <img className='widthFixImg' src="../image/banner-img.png" alt="" />
                <div className='allcircle'>
                    <div className='bigcircleList circle' ref={bigCirleRef}>
                        {/* <div className='bigcircle'></div> */}
                    </div>
                    <div className='smallcircleList circle' ref={smallCirleRef}>

                    </div>
                </div>
                
                <div className='center'>
                    <div className='center-img-box'>
                        <img className='center-text-img' src="../image/musicText.png" alt="" />
                        
                    </div>
                    <div className='text'>
                        Share & Music Online & Love Dj
                    </div>
                    <div className='btn'>
                        LET LISTEN NOW
                    </div>
                </div>
            </div>
        </div>
    )
}

export default SwiperBanner

将问题刨析开为
1.创建大小球
2.大小球动画
3.小球吸附大球轨迹

细节之颜色随机,只需要更换rgb值即可,这边是定义colorRandom函数去实现的

const colorRandom = () => {
        var r = Math.floor(Math.random() * 256); //随机生成256以内r值
        var g = Math.floor(Math.random() * 256); //随机生成256以内g值
        var b = Math.floor(Math.random() * 256); //随机生成256以内b值
        return `rgb(${r}, ${g}, ${b})`; //返回rgb(r,g,b)格式颜色
    }

下一部分内容打算直接写完了,包括过渡动画板块,已经一些part和音乐插件的使用
在这里插入图片描述

在这里插入图片描述

找到工作了,年后要上班了,好起来了

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值