老实说pixi虽然之前拿来做个几个简单的游戏,但是是好久前的了,又忘了,现在算是重新入门。
官网版本已经更新到v8去了,而react相关的pixi库pixi-react 虽然支持react18 但还是v6-v7的版本,既然已经看了v8的文档,那就没必要等pixi-react了,直接照着pixi文档撸吧。
先拉个react的vite脚手架下来(vue react都无所谓,基本上用不到框架或者库的api),在我的理解里,pixi每时每刻都是在内部进行不停的渲染绘制,所以跟react的render无关,同样也不需要vue的响应式视图。基本上如果更改了pixi元素的属性,下一帧就给你绘制出来了。
步入正题
安装pixi.js
pnpm add pixi.js
顺便装个gsap动画库吧
pnpm add gsap
按照官网文档初始化pixi
import { Application, Renderer, Graphics, Container, Point } from "pixi.js";
...
const ctx = useRef<HTMLDivElement | null>(null);
async function init() {
const app = new Application();
await app.init({
background: '#fff',
width: 400,
height: 400,
});
return Promise.resolve(app);
}
useEffect(() => {
init().then((app) => {
// 把pixi挂载到页面上
ctx.current?.appendChild(app.canvas);
});
}, [])
return (<div className={clock.container} ref={ctx}></div>)
接下来开始绘制时钟的基本轮廓
function drwaClock(app: Application<Renderer>) {
const clock = new Container();
// 秒钟
const secondContainer = new Container();
// 分钟
const minter = new Container();
// 小时
const hours = new Container();
clock.addChild(hours);
clock.addChild(minter);
clock.addChild(secondContainer);
const circle = new Graphics();
circle
.circle(app.screen.width / 2, app.screen.height / 2, 100)
.fill("#33333350")
.stroke({ width: 10, color: "#FFD5AE" });
clock.addChild(circle);
const second = new Graphics().rect(0, 0, 3, 100).fill("#000");
secondContainer.addChild(second);
// 设置矩形的旋转点为其宽度中心点和高度的85%
second.pivot.set(1.5, 85);
// 为围绕重点渲染 需要改变矩形的位置便于旋转效果
second.position.set(app.screen.width / 2 + 1.5, app.screen.height / 2);
// second.rotation = 6 * (Math.PI / 180);
const positionPiovt = second.toGlobal(new Point(1.5, 85));
// 矩形的中心点创建一个圆
const pivotCircle = new Graphics().circle(0, 0, 4).fill("#FFD5AE").stroke({ width: 2, color: "#000" });
pivotCircle.position.set(positionPiovt.x, positionPiovt.y);
secondContainer.addChild(pivotCircle);
// 添加分钟指针
const min = new Graphics().rect(0, 0, 4, 80).fill("#333");
min.pivot.set(2, 65);
min.position.set(app.screen.width / 2 + 2, app.screen.height / 2);
minter.addChild(min);
// 添加时钟指针
const hour = new Graphics().rect(0, 0, 4, 60).fill("#111");
hour.pivot.set(2, 45);
hour.position.set(app.screen.width / 2 + 2, app.screen.height / 2);
hours.addChild(hour);
const date = new Date();
// 1s是6° 即 6 * (Π/180)
const seconds = date.getSeconds();
second.rotation = seconds * 6 * (Math.PI / 180);
min.rotation = date.getMinutes() * 6 * (Math.PI / 180);
hour.rotation = date.getHours() * 6 * (Math.PI / 180);
const calibration = addCalibration(app);
clock.addChild(calibration);
app.stage.addChild(clock);
app.ticker.add((delte) => {
second.rotation += 6 * (Math.PI / 180) * (delte.deltaTime / 60);
if (second.rotation >= Math.PI * 2) {
second.rotation %= Math.PI * 2;
gsap.to(min, {
rotation: `+= ${6 * (Math.PI / 180)}`,
});
}
if (min.rotation >= Math.PI * 2) {
min.rotation %= Math.PI * 2;
gsap.to(hour, {
rotation: `+= ${6 * (Math.PI / 180)}`,
});
}
if (hour.rotation >= Math.PI * 2) {
hour.rotation %= Math.PI * 2;
}
});
}
这里添加了时钟分钟和秒钟以及时钟的边框,在ticker里每秒钟转动6°,超过360°时重新开始计算,并设置分钟转动6°;
下面添加时钟的刻度addCalibration
// 刻度
function addCalibration(app: Application<Renderer>) {
const calibration = new Container();
const zero = new Graphics().rect(0, 0, 4, 12).fill("#000");
zero.position.set(app.screen.width / 2, app.screen.height / 2 - 105);
calibration.addChild(zero);
const three = new Graphics().rect(0, 0, 4, 12).fill("#000");
three.rotation = Math.PI / 2;
three.position.set(app.screen.width / 2 + 105, app.screen.height / 2);
calibration.addChild(three);
const six = new Graphics().rect(0, 0, 4, 12).fill("#000");
six.pivot.set(2, 12);
six.position.set(app.screen.width / 2, app.screen.height / 2 + 105);
calibration.addChild(six);
const nine = new Graphics().rect(0, 0, 4, 12).fill("#000");
nine.rotation = Math.PI / 2;
nine.pivot.set(2, 12);
nine.position.set(app.screen.width / 2 - 105, app.screen.height / 2);
calibration.addChild(nine);
return calibration;
}
时间关系 就不把剩下的刻度标出来了,最终效果为