有趣的前端项目——一个暴躁萌的大眼仔

有趣的前端项目——一个暴躁萌的大眼仔

众所周知,我是一个摆子前端(真的 ),闲来无事,网上冲浪
遇见了如此蠢萌的大眼
在这里插入图片描述

于是我,行也思,坐也思,可算把这个大眼给复刻出来了。

原文出处:稀土掘金的一位博主——Urias
稀土掘金Urias

晓看天色暮看云,行也思君,坐也思君。 ——一剪梅·唐寅

同样,今天周一,在下也与诸位同道中人以来探讨这个话题::「 当一个前端空闲的时候会做些什么 」。
原博主说的我很赞同,我也非常喜欢在空闲的时间钻研( )前端技术(工作量 ) 。

先来看看我这的大眼萌萌的介绍:

  • 名称:大眼
  • 生成:2022-8-29
  • 性别:无性
  • 情绪:发怒/常态
  • 状态:休眠/工作
  • 简介:除了很萌,就是很萌,像猫猫一样,喜欢一直盯着你的鼠标,以防你找不到你的鼠标。但是大眼猫有起床气,而且非常懒散,容易犯困。

大眼的生活照:

在这里插入图片描述

✨画“大眼”,先画圆

“画人先画骨”,同样画大眼也得先画它的骨,没错,以普遍理性而论就是一个圆

还是和正常思路,先搞一个盒子,用来当骨

<div class="eyeSocket">
        </div>

在添加一些必要的样式

body {
    width: 100vw;
    height: 100vh;
    overflow: hidden;
    background-color: #111;
}

.eyeSocket {
    position: absolute;
    left: calc(50% - 75px);
    top: calc(50% - 75px);
    width: 150px;
    /* 长宽比 1:1 如果浏览器不支持该属性,换成 height: 150px 也一样 */
    aspect-ratio: 1;
    border-radius: 50%;
    border: 4px solid var(--c-eyeSocket);
    z-index: 1;
}

效果:
在这里插入图片描述
然后就是两个圆和一些阴影效果,为了不影响HTML的结构,所以用两个伪元素来实现

.eyeSocket::before,
.eyeSocket::after {
    content: "";
    //绝对定位使其居中
    position: absolute;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%); 
    border-radius: 50%;
    box-sizing: border-box; 
}
.eyeSocket::before {
    width: calc(100% + 20px);
    height: calc(100% + 20px);
    border: 6px solid #02ffff;
}
.eyeSocket::after {
    width: 100%;
    height: 100%;
    border: 4px solid rgb(35, 22, 140);
    box-shadow: inset 0px 0px 30px rgb(35, 22, 140);
}

在这里插入图片描述

✨创龙点睛

大眼的眼球()画好之后,就需要给他点上眼睛,想要什么样的样式因人而异,我就模仿原博主,采用仪表盘分割的方法来制作,分割线作为大眼的眼仁。

为了方便制作过度效果,小生使用的的是echarts来完成的。
echarts的引入方式非常简单

<script src="./js/echarts.js"></script>

接下来
在给眼仁一个容器,并且初始化echarts画布

<div class="eyeSocket">
		<div id="eyeball"></div>
	</div>
#eyeball {
	width: 100%;
	height: 100%;
}

画眼球

let eyeball = document.getElementById('eyeball');//获取元素
let eyeballChart = echarts.init(eyeball);//初始化画布

function getEyeballChart() {
    eyeballChart.setOption({
        series: [
            {
                type: 'gauge', // 使用仪表盘类型
                radius: '-20%', // 采用负数是为了让分割线从内向外延伸
                clockwise: false,
                startAngle: `${0 + leftRotSize * 5}`, // 起始角度
                endAngle: `${270 + leftRotSize * 5}`, // 结束角度
                splitNumber: 3, // 分割数量,会将270度分割为3份,所以有四根线
                detail: false,
                axisLine: {
                    show: false,
                },
                axisTick: false,
                splitLine: {
                    show: true,
                    length: ballSize, // 分割线长度
                    lineStyle: {
                        shadowBlur: 20, // 阴影渐变
                        shadowColor: ballColor, // 阴影颜色
                        shadowOffsetY: '0',
                        color: ballColor, // 分割线颜色
                        width: 4, // 分割线宽度
                    }
                },
                axisLabel: false
            },
            {
                type: 'gauge',
                radius: '-20%',
                clockwise: false,
                startAngle: `${45 + leftRotSize * 5}`,//倾斜角度
                endAngle: `${315 + leftRotSize * 5}`,
                splitNumber: 3,
                detail: false,
                axisLine: {
                    show: false,
                    length: ballSize,
                },
                axisTick: false,
                splitLine: {
                    show: true,
                    length: ballSize,
                    lineStyle: {
                        shadowBlur: 20, // 阴影渐变
                        shadowColor: ballColor, // 阴影颜色
                        shadowOffsetY: '0',
                        color: ballColor, // 分割线颜色
                        width: 4,
                    }
                },
                axisLabel: false
            }
        ]
    });
}


//getEyeballChart() 可以解除注释看看效果

效果
在这里插入图片描述

这样,眼仁就画好了,小生并不经常使用echarts,所以画起来很费劲,但对于经常使用echarts的同学可以说是轻而易举。

以上
静态大眼已经画完,接下来要为大眼赋予生命周期了

✨生命仪式: 休眠状态

赋予生命是神圣的,她需要一个过程,所以小生从最简单的状态开始——「 休眠 」

休眠状态其实就是睡觉,自然是闭着眼睛睡觉,所以小生只有设计不露出眼仁的同时有节奏的呼吸(缩放)即可,相比于整个生命仪式来说,是较简单的一部分。

呼吸

小生使用的是 Css转换+动画的样式

<div class="eyeSocket eyeSocketSleeping">
		<div id="eyeball"></div>
	</div>
.eyeSocketSleeping {
    animation: sleeping 4s infinite;
}


@keyframes sleeping {
    0% {
        transform: scale(1);
    }

    50% {
        transform: scale(1.2);
    }

    100% {
        transform: scale(1);
    }
}

闭眼

既然是休眠,那一定有闭眼状态,所及接下就要想办法把大眼的眼睛闭上。之前用echarts设置的radius的值为负数,因为分割线是从内向外伸长的,所有,闭眼的动作就相当于慢慢减小分割线的高度,即可实现眼睛慢慢缩小的效果

**过程:**大眼慢慢闭上眼睛(分割线缩小至0),然后开始 呼吸

<div class="eyeSocket eyeSocketSleeping" id="bigEye">
		<div id="eyeball"></div>
	</div>
let bigEye = document.getElementById('bigEye'); // 获取元素


let rotTimer; // 定时器
let leftRotSize = 0; // 旋转角度
let ballSize = 0; // 眼睛尺寸


function getEyeballChart() {
    eyeballChart.setOption({
        series: [
            {
                //其他代码
                startAngle: `${0 + leftRotSize * 5}`, // 起始角度
                endAngle: `${270 + leftRotSize * 5}`, // 结束角度
                //其他代码
                splitLine: {
                    show: true,
                    length: ballSize, // 分割线长度
                    //其他代码
                },
                axisLabel: false
            },
            {
                //其他代码
                startAngle: `${45 + leftRotSize * 5}`,//倾斜角度
                endAngle: `${315 + leftRotSize * 5}`,
                //其他代码
                splitLine: {
                    show: true,
                    length: ballSize,
                   //其他代码
                },
                axisLabel: false
            }
        ]
    });
}


// 休眠
function toSleep() {
    isSleep = true;
    clearInterval(rotTimer); // 清除定时器
    rotTimer = setInterval(() => {
        getEyeballChart()
        if (ballSize > 0) {
            ballSize -= 0.1; // 当眼球存在时慢慢减小
        } else {
            bigEye.className = 'eyeSocket eyeSocketSleeping'; // 眼球消失后添加呼吸
        }
        leftRotSize === 360 ? (leftRotSize = 0) : (leftRotSize += 0.1); // 旋转,
    }, 10);
    }

旋转的实现原理:(小生去学习了原作者另一篇关于动效的文章,看了一下还是蛮简单)

「 修改每个圆的起始角度(startAngle)和结束角度(endAngle),并不断的刷新视图」
「 增加度数为逆时针旋转,减去度数为顺时针旋转」

如此以来就实现了眼睛缩小消失,然后开始呼吸过程,同时咱们的大眼也进入了生命仪式之「休眠状态」
在这里插入图片描述

✨生命仪式: 起床气状态

小生很赞同心理学上的说法: 情绪会让你接近生命的本真。

生命不就是情绪的结合嘛?没有情绪怎么能被称为生命呢?

在设置起床气的状态之前,我还需要做点准备工作,让大眼处于休眠状态

通过修改类名

<div class="eyeSocket eyeSocketSleeping" id="bigEye">
            <div id="eyeball"></div>
        </div>

更改眼球的尺寸

let ballSize = 0; // 眼睛尺寸

唤醒

为了能够给唤醒大眼,小生需要准备一个唤醒动作——点击事件

let isSleep = true; // 是否处于休眠状态


// 点击
bigEye.addEventListener('click', () => {
//判断是否休眠
    if (!isSleep) return;
    //执行唤醒
    clickToWakeup();
})

// 唤醒
function clickToWakeup() {
    isSleep = false;
    eyeFilter.className = bigEye.className = 'eyeSocket eyeSocketLookging';
    clearInterval(rotTimer);
    rotTimer = setInterval(() => {
        getEyeballChart()
        ballSize <= 50 && (ballSize += 1);
        leftRotSize === 360 ? (leftRotSize = 0) : (leftRotSize += 0.5);
    }, 10);
}

这样,小生点击一下,大眼就苏醒过来了

生气

但是!

这是一个没有情绪的大眼,而且小生想要的是一个有起床气的大眼所以这样的大眼咱们不要!

退!退!退!退!退!

···

我们只需要修改一下唤醒他之后的操作,给他添加上起床气就行了。






接着来吧

首先,小生先把大眼的蓝色抽离出来,使用CSS变量代替,然后再苏醒后给他添加代表生气的红色就行了

body {
	/*其他属性*/
	
    perspective: 1000px;
    --c-eyeSocket: rgb(41, 104, 217);
    --c-eyeSocket-outer: #02ffff;
    --c-eyeSocket-outer-shadow: transparent;
    --c-eyeSocket-inner: rgb(35, 22, 140);
}

.eyeSocket {
	/*其他属性*/
	
    border: 4px solid var(--c-eyeSocket);
    box-shadow: 0px 0px 50px var(--c-eyeSocket-outer-shadow);
    transition: border .5s ease-in-out, box-shadow .5 ease-in-out;
    z-index: 1;
}
.eyeSocket::before,
.eyeSocket::after {
	/*其他属性*/
	
    transition: all .5s ease-in-out;
}
.eyeSocket::before {
	/*其他属性*/
	
    border: 6px solid var(--c-eyeSocket-outer);
}

.eyeSocket::after {
	/*其他属性*/
	
    border: 4px solid var(--c-eyeSocket-inner);
    box-shadow: inset 0px 0px 30px var(--c-eyeSocket-outer-shadow);
}

JS

function getEyeballChart() {
    eyeballChart.setOption({
        series: [
            {
				//其他代码
				
                splitLine: {
				//其他代码
				
                    lineStyle: {
						//其他代码
						
						shadowColor:ballColor,
                        color: ballColor, // 分割线颜色

                    }
                },
                axisLabel: false
            },
            {
				//其他代码
				
                axisLine: {
				//其他代码
				
                },
				//其他代码
				
                splitLine: {
				//其他代码
				
                    lineStyle: {
						//其他代码
                        shadowColor: ballColor, // 阴影颜色
						//其他代码
                        color: ballColor, // 分割线颜色

                    }
                },
				//其他代码
				            }
        ]
    });
}

//样式
//生气模式
function setAngry() {
    // 通过js修改body的css变量
    document.body.style.setProperty('--c-eyeSocket', 'rgb(255,187,255)');
    document.body.style.setProperty('--c-eyeSocket-outer', 'rgb(238,85,135)');
    document.body.style.setProperty('--c-eyeSocket-outer-shadow', 'rgb(255, 60, 86)');
    document.body.style.setProperty('--c-eyeSocket-inner', 'rgb(208,14,74)');
    ballColor = 'rgb(208,14,74)';
}
// 常态模式
function setNormal() {
    document.body.style.setProperty('--c-eyeSocket', 'rgb(41, 104, 217)');
    document.body.style.setProperty('--c-eyeSocket-outer', '#02ffff');
    document.body.style.setProperty('--c-eyeSocket-outer-shadow', 'transparent');
    document.body.style.setProperty('--c-eyeSocket-inner', 'rgb(35, 22, 140)');
    ballColor = 'rgb(0,238,255)';
}

//唤醒函数
function clickToWakeup() {
	//其他代码
    setAngry(); // 设置为生气模式
	//其他代码
    rotTimer = setInterval(() => {
	//其他代码
    }, 10);
}

大眼生气长这个样子:





在这里插入图片描述

更生气




不知道在座(站?蹲?)各位是如何看待,但是对小生来说,感觉这样大眼还是不够生气。



没错还不够生气



说干就干

小生这里是模仿了大佬的svg滤镜的方法,svg滤镜的属性和使用方法非常的多,小生用的也不是很娴熟,小生在本本中就不再赘述了,网上教程很多。

小生见大佬用的是feTurbulence来形成噪声,然后用feDisplacementMap替换,来给大眼添加粒子效果,但feDisplacementMap会混合掉元素,所以小生需要一个大眼的替身来代替大眼被融合。

创建大眼替身

        <div class="filter">
            <div class="eyeSocket" id="eyeFilter"></div>
        </div>

CSS


.filter {
    width: 100%;
    height: 100%;
    filter: url('#filter');
}

.eyeSocket,
.filter .eyeSocket {
    position: absolute;
    left: calc(50% - 75px);
    top: calc(50% - 75px);
    width: 150px;
    /* 长宽比 1:1 如果浏览器不支持该属性,换成 height: 150px 也一样 */
    aspect-ratio: 1;
    border-radius: 50%;
    border: 4px solid var(--c-eyeSocket);
    box-shadow: 0px 0px 50px var(--c-eyeSocket-outer-shadow);
    transition: border .5s ease-in-out, box-shadow .5 ease-in-out;
    z-index: 1;
}

.filter .eyeSocket {
    opacity: 0;
    left: calc(50% - 92px);
    top: calc(50% - 92px);
    transition: all 0.5s ease-in-out;
}

融合

HTML

<div class="filter">
            <div class="eyeSocket" id="eyeFilter"></div>
        </div>
        <!-- Svg滤镜 -->
        <svg width="0">
            <filter id='filter'>
                <feTurbulence baseFrequency="1">
                    <animate id="animate1" attributeName="baseFrequency" dur="1s" from="0.5" to="0.55"
                        begin="0s;animate1.end">
                    </animate>
                    <animate id="animate2" attributeName="baseFrequency" dur="1s" from="0.55" to="0.5"
                        begin="animate2.end">
                    </animate>
                </feTurbulence>
                <feDisplacementMap in="SourceGraphic" scale="50" xChannelSelector="R" yChannelSelector="B" />
            </filter>
        </svg>

在这里插入图片描述
芜湖,果然献祭了一只大眼,成品的效果还不错。
看起来酷炫多了

邪王真眼!

在这里插入图片描述
在这里插入图片描述
接下来将这个粒子光辉和大眼本体对齐





额···

本质上是因为feDisplacementMap设置了scale属性的原因。
feDisplacementMap其实就是一个位置替换滤镜,通过就是改变元素和图形的像素位置的进行重新映射,然后替换一个新的位置,形成一个新的图形。
而scale就是替换公式计算后偏移值相乘的比例,影响着图形的偏移量和呈现的效果。

但是话虽如此,咱这个光环不能真的就这么戴着呀,咱们还需要对光环的位置进行一些微调。

    left: calc(50% - 92px);
    top: calc(50% - 92px);

在这里插入图片描述

ENMMMM····

这下光环也有了,看起来确实比之前生气多了。



但是我还需要对大眼做一些调整,因为大眼在常规状态并不需要这个光环,睡着的时候这光环在旁边“滋啦滋啦”的,很吵的慌,所以我们需要把在常态下把光环去掉。


小生用的是opacity属性来控制,当大眼处于生气状态时,光环为不透光,处于常规状态时,光环透明不可见。

CSS

    opacity: 0;
    left: calc(50% - 92px);
    top: calc(50% - 92px);
    transition: all 0.5s ease-in-out;

JS

function clickToWakeup() {
//其他代码
	eyeFilter.style.opacity = '1';
	//其他代码
	}

这样设置完成,一个更生气的大眼就完成了


EMMMMMMMMMMMMMMMMMMMM···

《好像还是不够》

更更生气邪王真眼!

不知道各位觉得如何,但是小生认为,一个真正生气的大眼,不止局限于自己生气,还要找别人发泄!!
所以小生给大眼添加了找人的动效

当然他是找不到的,他那么笨

其实就是通过大眼的左右旋转,通过CSS来实现。

CSS

body{
	perspective:1000px
}

.eyeSocketLookging {
    animation: lookAround 2.5s;
}
@keyframes lookAround {
    0% {
        transform: translateX(0) rotateY(0);
    }

    10% {
        transform: translateX(0) rotateY(0);
    }

    40% {
        transform: translateX(-70px) rotateY(-30deg);
    }

    80% {
        transform: translateX(70px) rotateY(30deg);
    }

    100% {
        transform: translateX(0) rotateY(0);
    }
}

JS

let eyeFilter = document.getElementById('eyeFilter')

// 唤醒
function clickToWeakup() {
	//其他代码
    eyeFilter.className = bigEye.className = 'eyeSocket eyeSocketLookging';
	//其他代码
    rotTimer = setInterval(() => {
    //其他代码
    }, 10);
}
// 点击
bigEye.addEventListener('click', () => {
    if (!isSleep) return;
    // console.log('a');
    clickToWeakup();
})

在这里插入图片描述

向左看时,Y轴的偏移量为-70px,同时按Y轴旋转-30°
向右看时,Y轴的偏移量为70px,同时按Y轴旋转30°

✨ 生命仪式:自我调整状态

这个状态非常好解释,大眼虽然有起床气,但是也仅局限于起床对吧,总不能一直让他生气,气坏了可咋办 重写一个
带着情绪工作,效果也不好,不是吗?




所以我们还需要给他点时间,让他调整一下自我状态,恢复正常。

这个自我调整状态就一个从生气变为常态的过程。
在这个过程,大眼需要将代表生气的红色变更为常态的蓝色,同时红眼也会慢慢的褪去。



但这个自我调整状态还是属于唤醒状态中,只是需要放在起床气之后。

思路

1、退出起床气的状态。
2、变回常态

为了保证这两个步骤的先后顺序,可以使用Promise,不懂Promise的同学可以去学习一下。小生也说不清楚。

// 监听动画结束
bigEye.addEventListener('webkitAnimationEnd', () => {
    console.log('aa');
    new Promise(res => {
        clearInterval(rotTimer);
        rotTimer = setInterval(() => {
            getEyeballChart();//更新视图
            ballSize > 0 && (ballSize -= 0.5);
            leftRotSize === 360 ? (leftRotSize = 0) : (leftRotSize += 0.1);
            if (ballSize === 0) {
                clearInterval(rotTimer);
                res();
            }
        }, 10);
    }).then(() => {
        eyeFilter.style.opacity = '0';
        eyeFilter.className = bigEye.className = 'eyeSocket';
        setNormal();//设置常态
        document.body.addEventListener('mousemove', focusOnMouse);
        rotTimer = setInterval(() => {
            getEyeballChart();
            ballSize <= 12 && (ballSize += 0.1);
            leftRotSize === 360 ? (leftRotSize = 0) : (leftRotSize += 0.1);
        }, 10);
    });
});

添加了这样一个监听事件后,咱们的大眼就已经具备了自我调整的能力了:

在这里插入图片描述

✨ 生命仪式:工作状态

接下就是大眼的重中之重,也就是大眼的工作状态

小生给大眼的工作特别简单,就是盯着我的鼠标指针。

盯着,不只是说说而已,要如何才能表现出大眼已经盯住了呢?

小生的思路是;

1、以大眼的位置为原点建立一个直角坐标系
2、然后通过监听鼠标移动事件,获取鼠标所在位置,计算出鼠标处于大眼坐标系的坐标。
3、将整个视口背景以X轴和Y轴进行等分成无数个旋转角度,通过鼠标坐标的数值和正负来调整大眼眼框和眼睛的Y轴和Z轴旋转,从而达到盯住鼠标的目的。

好,接下来理清思路,我们付诸行动

//工作
function focusOnMouse(e) {
    // 视口大小,获取整个视口大小
    let clientWidth = document.body.clientWidth;
    let clientHeight = document.body.clientHeight;
    // 建立原点 以大眼为原点
    let origin = [clientWidth / 2, clientHeight / 2];
    // 鼠标坐标
    let mouseCoords = [e.clientX - origin[0], origin[1] - e.clientY];

    // 旋转角度
    let eyeXDeg = mouseCoords[1] / clientHeight * 80;
    let eyeYDeg = mouseCoords[0] / clientWidth * 60;
    bigEye.style.transform = `rotateY(${eyeYDeg}deg) rotateX(${eyeXDeg}deg)`;
    eyeball.style.transform = `translate(${eyeYDeg / 1.5}px, ${-eyeXDeg / 1.5}px)`;
    //设置休眠
    if (sleepTimer) clearTimeout(sleepTimer);
    sleepTimer = setTimeout(() => {
        toSleep();
    }, 30000);
}




注意: 如果觉得旋转角度不够大,可以调整代码中的80和60,最大可以到180,也就是完全朝向,但是由于大眼终归是一个平面生物,如果旋转度数过大,就很容易穿帮,如果旋转角度为180,大眼就会在某个方向完全消失看不见(因为大眼没有厚度,这个也许是可以优化的点),所以个人喜好调整吧。

✨ 生命仪式:懒惰状态

顾名思义,懒惰状态就是···懒惰状态。

小生在给大眼设计的懒惰状态就是当在下的鼠标超过30秒没有移动时,大眼就会进入休眠状态。

所以生命仪式的最后收尾其实非常的轻松,没有大量的代码,只需要添加一个定时器,然后修改休眠状态的代码,将大眼的所有参数初始化即可。


let sleepTimer;//休眠定时器


// 休眠
function toSleep() {
	//其他操作
    document.body.removeEventListener('mousemove', focusOnMouse);
    bigEye.style.transform = `rotateY(0deg) rotateX(0deg)`;
    eyeball.style.transform = `translate(0px, 0px)`;
}

//工作
function focusOnMouse(e) {
 	//其他操作
    //设置休眠
    if (sleepTimer) clearTimeout(sleepTimer);
    sleepTimer = setTimeout(() => {
        toSleep();
    }, 30000);
}


✨咱们萌萌的大眼仔

在这里插入图片描述

  • 5
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 3
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

玄东林檎

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值