一、前言|项目概述
在前端视觉动画体系中,Canvas 像素级自定义绘制是实现高自由度动态图形效果的核心技术。相较于传统 CSS 动画,Canvas 能够实现更细腻的渐变光影、轨迹旋转、分层渲染以及动态帧率控制,非常适合制作圆形肌理、瞳孔动态、光影流动类特效。
本次项目基于原生HTML5 + CSS3 + JavaScript Canvas + python从零复刻经典写轮眼动态视觉效果。
代码拆分为两个完整版本:
- 精简版:逻辑轻量化、代码极简,适合入门新手练手,
- 高级复刻版:多层瞳孔分层、真实光影渐变、抗锯齿渲染、动态绘制、细节还原度更高
全程无任何第三方库、原生前端实现,可直接本地双击运行、部署网页、二次改造拓展。
二、效果展示
a.精简版效果图
效果特点:
- 正圆画布抗锯齿渲染,边缘平滑无锯齿
- 多层瞳纹独立旋转,内外层差异化转速
- 高级版搭载正弦函数明暗光影浮动
- 纯黑沉浸背景,外发光氛围感
- requestAnimationFrame 稳定高帧动画,流畅不卡顿
- 支持自定义尺寸、旋转速度、配色、纹路参数
b.高级复刻版效果图
三、技术核心解析
3.1 Canvas 分层绘制思想
写轮眼高级质感的核心是分层渲染:
- 外层:眼白外环 / 外发光轮廓
- 中层:红色径向渐变基底
- 纹路层:勾玉瞳纹动态旋转层
- 中心层:黑色核心瞳孔 + 高光点缀
每一层独立绘制、独立角度更新,图层叠加形成眼球动态质感。
3.2 动画实现原理
- 使用
requestAnimationFrame实现原生高帧动画 - 每一帧清空画布,重绘全部图层
- 通过角度变量自增,实现不同图层差异化旋转
- 高级版加入正弦函数,实现明暗呼吸光影浮动
3.3 视觉优化要点
- 画布抗锯齿处理
- 径向渐变替代纯色填充,提升质感
- 圆心精准对齐,保证绝对正圆
- 清除浏览器默认边距,全屏居中展示
四、完整源码(双版本)
4.1 精简版源码(新手入门)
<!DOCTYPEhtml><htmllang="zh-CN"><head><metacharset="UTF-8"><title>写轮眼</title><style>*{margin:0;padding:0;box-sizing:border-box;}body{background-color:#000;display:flex;justify-content:center;align-items:center;height:100vh;}/* 写轮眼外层容器 */.sharingan{width:220px;height:220px;border-radius:50%;background-color:#b80000;border:8px solid #000;position:relative;cursor:pointer;box-shadow:0 0 30px #900;}/* 中间黑圈 */.center-circle{width:70px;height:70px;background:#000;border-radius:50%;position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);z-index:2;}/* 勾玉旋转环 */.tomoe-wrap{width:100%;height:100%;position:absolute;animation:rotate 6s linear infinite;}/* 单个勾玉样式 */.tomoe{width:50px;height:90px;background:#000;border-radius:0 50px 0 50px;position:absolute;transform-origin:center 110px;}/* 三个勾玉分别旋转定位 */.tomoe:nth-child(1){transform:rotate(0deg);}.tomoe:nth-child(2){transform:rotate(120deg);}.tomoe:nth-child(3){transform:rotate(240deg);}@keyframesrotate{from{transform:rotate(0deg);}to{transform:rotate(360deg);}}</style></head><body><divclass="sharingan"id="eye"><divclass="center-circle"></div><divclass="tomoe-wrap"><divclass="tomoe"></div><divclass="tomoe"></div><divclass="tomoe"></div></div></div><script>consteye=document.getElementById('eye');eye.addEventListener('click',()=>{constwrap=document.querySelector('.tomoe-wrap');// 点击切换快慢if(wrap.style.animationDuration==="1s"){wrap.style.animationDuration="6s";}else{wrap.style.animationDuration="1s";}})</script></body></html>4.2 高级复刻版源码(质感拉满)
import turtle as t t.pensize(2) t.penup() t.goto(0,-50) t.pendown() t.color("black") t.begin_fill() t.circle(50) t.color("red") t.end_fill() t.penup() t.goto(0,-10) t.pendown() t.color("black") t.circle(10) t.penup() t.goto(0,10) t.pendown() t.seth(90) t.begin_fill() t.fd(40) t.seth(175) t.circle(40,-120) t.seth(65) t.circle(35,100) t.seth(260) t.fd(25) t.end_fill() t.penup() t.goto(10,0) t.pendown() t.seth(90) t.circle(10,-30) t.seth(330) t.begin_fill() t.fd(40) t.seth(53) t.circle(40,-120) t.seth(302) t.circle(35,100) t.seth(140) t.fd(25) t.end_fill() t.penup() t.goto(-10,0) t.pendown() t.seth(270) t.circle(10,30) t.begin_fill() t.seth(210) t.fd(40) t.seth(294) t.circle(40,-120) t.seth(183) t.circle(35,100) t.seth(19) t.fd(25) t.end_fill() t.penup() t.goto(-120,100) t.pendown() t.color("black") t.begin_fill() t.seth(320) t.fd(15) t.seth(9) t.fd(160) t.seth(6) t.fd(100) t.penup() t.goto(-120,100) t.pendown() t.seth(9) t.fd(160) t.seth(1) t.fd(110) t.color("gainsboro") t.end_fill() t.color("oldlace") t.penup() t.goto(0,200) t.pendown() t.begin_fill() t.seth(266) t.fd(20) t.seth(267) t.fd(20) t.seth(268) t.fd(20) t.seth(269) t.fd(80) t.seth(270) t.fd(8) t.penup() t.goto(0,200) t.pendown() t.seth(273) t.fd(70) t.seth(272) t.fd(30) t.seth(271) t.fd(20) t.seth(270) t.fd(28) t.seth(180) t.fd(10) t.color("floralwhite") t.end_fill() t.penup() t.goto(-105,-45) t.pendown() t.color("black") t.pensize(3) t.seth(45) t.fd(9) t.seth(48) t.fd(6) t.seth(66) t.fd(15) t.seth(62) t.fd(30) t.seth(56) t.fd(25) t.seth(35) t.fd(20) t.seth(20) t.fd(20) t.seth(15) t.fd(20) t.seth(10) t.fd(20) t.seth(5) t.fd(30) t.seth(0) t.fd(35) t.seth(5) t.fd(40) t.penup() t.goto(-98,-47) t.pendown() t.seth(358) t.fd(6) t.seth(340) t.fd(10) t.seth(357) t.fd(10) t.seth(358) t.fd(25) t.seth(359) t.fd(20) t.seth(0) t.fd(20) t.seth(3) t.fd(10) t.seth(4) t.fd(5) t.seth(9) t.fd(5) t.seth(16) t.fd(5) t.seth(22) t.fd(5) t.seth(30) t.fd(10) t.seth(45) t.fd(20) t.seth(55) t.fd(60) t.seth(60) t.fd(28) t.seth(55) t.fd(9) t.penup() t.goto(-105,-43) t.seth(45) t.fd(9) t.seth(48) t.fd(6) t.pendown() t.seth(66) t.fd(15) t.seth(62) t.fd(30) t.seth(56) t.fd(25) t.seth(35) t.fd(20) t.seth(20) t.fd(20) t.seth(15) t.fd(20) t.seth(10) t.fd(20) t.seth(5) t.fd(30) t.seth(0) t.fd(35) t.seth(5) t.fd(35) t.penup() t.goto(-105,-41) t.seth(45) t.fd(9) t.seth(48) t.fd(6) t.pendown() t.seth(66) t.fd(15) t.seth(62) t.fd(30) t.seth(56) t.fd(25) t.seth(35) t.fd(20) t.seth(20) t.fd(20) t.seth(15) t.fd(20) t.seth(10) t.fd(20) t.seth(5) t.fd(30) t.seth(0) t.fd(35) t.seth(5) t.fd(29) t.penup() t.goto(-105,-39) t.seth(45) t.fd(9) t.seth(48) t.fd(6) t.pendown() t.seth(66) t.fd(15) t.seth(62) t.fd(30) t.seth(56) t.fd(25) t.seth(35) t.fd(20) t.seth(20) t.fd(20) t.seth(15) t.fd(20) t.seth(10) t.fd(20) t.seth(5) t.fd(30) t.seth(0) t.fd(35) t.seth(5) t.fd(24) t.penup() t.goto(-98,-49) t.seth(358) t.fd(6) t.pendown() t.seth(340) t.fd(10) t.seth(357) t.fd(10) t.seth(358) t.fd(25) t.seth(359) t.fd(20) t.seth(0) t.fd(20) t.seth(3) t.fd(10) t.seth(4) t.fd(5) t.seth(9) t.fd(5) t.seth(16) t.fd(5) t.seth(22) t.fd(5) t.seth(30) t.fd(10) t.seth(45) t.fd(20) t.seth(55) t.fd(60) t.seth(60) t.fd(28) t.seth(55) t.fd(9) t.penup() t.color("oldlace") t.goto(0,-162) t.pendown() t.pensize(2) t.begin_fill() t.seth(94) t.fd(20) t.seth(93) t.fd(20) t.seth(92) t.fd(50) t.seth(91) t.fd(15) t.penup() t.goto(0,-162) t.pendown() t.seth(86) t.fd(50) t.seth(87) t.fd(20) t.seth(88) t.fd(10) t.seth(89) t.fd(25) t.seth(180) t.fd(8) t.color("floralwhite") t.end_fill() t.penup() t.goto(5,-48) t.pendown() t.color("black") t.begin_fill() t.seth(4) t.fd(3) t.seth(9) t.fd(5) t.seth(16) t.fd(5) t.seth(22) t.fd(5) t.seth(30) t.fd(10) t.seth(45) t.fd(20) t.seth(55) t.fd(20) t.seth(60) t.fd(3) t.seth(20) t.fd(2) t.seth(0) t.fd(2) t.seth(350) t.fd(3) t.seth(300) t.fd(3) t.seth(235) t.fd(30) t.seth(245) t.fd(15) t.seth(255) t.fd(10) t.seth(260) t.fd(15) t.seth(265) t.fd(40) t.seth(268) t.fd(40) t.seth(270) t.fd(45) t.seth(280) t.fd(7) t.seth(285) t.fd(7) t.seth(290) t.fd(5) t.seth(285) t.fd(5) t.seth(275) t.fd(5) t.seth(270) t.fd(3) t.seth(260) t.fd(7) t.seth(220) t.fd(5) t.seth(180) t.fd(5) t.seth(120) t.fd(5) t.seth(105) t.fd(5) t.seth(95) t.fd(5) t.seth(90) t.fd(3) t.seth(85) t.fd(3) t.seth(80) t.fd(5) t.seth(85) t.fd(5) t.seth(90) t.fd(65) t.seth(92) t.fd(35) t.seth(95) t.fd(30) t.seth(110) t.fd(15) t.seth(140) t.fd(10) t.seth(180) t.fd(8) t.seth(140) t.fd(3) t.seth(110) t.fd(3) t.seth(90) t.fd(2) t.seth(60) t.fd(2) t.seth(10) t.fd(5) t.color("red") t.end_fill() t.penup() t.goto(180,-180) t.pensize(2) t.color("black") t.pendown() t.seth(0) t.circle(10,-60) t.circle(12,-60) t.circle(14,-60) t.circle(16,-60) t.circle(18,-60) t.circle(20,-60) t.circle(22,-60) t.circle(24,-60) t.circle(26,-90) t.seth(45) t.fd(15) t.penup() t.goto(160,-175) t.pendown() t.seth(240) t.fd(20) t.seth(0) t.fd(20) t.penup() t.goto(300,300)五、运行教程
- 复制对应版本全部代码于pycharm中运行
- 新建文本文档,粘贴代码
- 另存为
.html文件,编码选择 UTF-8 - 双击文件,使用浏览器直接打开
无需服务器、无需依赖包、无需环境配置,纯静态页面,电脑端通用。
六、自定义拓展参数
- 修改眼球大小:同步修改 canvas 的 width、height
- 修改旋转速度:调整角度自增变量,数值越大转动越快
- 修改瞳色:修改
fillStyle、createRadialGradient色值,可改造为轮回眼、万花筒等风格 - 关闭外发光:删除 CSS 中 box-shadow 光晕代码
七、二次开发拓展思路
- 鼠标跟随视角,眼球跟随鼠标转动
- 增加开眼渐入、闭眼收瞳过渡动画
- 增加交互,点击切换不同瞳纹样式
- 移动端自适应缩放
- 作为个人主页动态背景
八、总结
本篇分享 Canvas,python 写轮眼双版本源码,覆盖入门精简版与高阶复刻版,适合练习 Canvas 图形绘制、前端html展示、分层动画、帧渲染逻辑。代码干净无冗余,可用于学习、展示、二次开发。如果对你有帮助,欢迎点赞收藏,评论区交流!