Files
blog-admin-ui/src/utils/blog/ribbon.ts
2025-09-21 22:48:33 +08:00

165 lines
3.6 KiB
TypeScript

const { random } = Math
const randomColor = [
['#95e1d3', '#eaffd0', '#fce38a', '#f38181'],
['#6a2c70', '#b83b5e', '#f08a5d', '#f9ed69'],
['#edb1f1', '#d59bf6', '#9896f1', '#8ef6e4'],
['#ff9de2', '#8c82fc', '#b693fe', '#7effdb'],
['#fff5a5', '#ffaa64', '#ff8264', '#ff6464']
]
export interface IRibbonOptions {
zIndex: number;
alpha: number;
size: number;
}
interface IRibbonPoint {
x: number;
y: number;
}
const getRandomInt = (value: number) => {
return Math.floor(Math.random() * value)
}
export class Ribbon {
/**
* 画布元素
*/
canvas: HTMLCanvasElement;
/**
* Canvas中的Context
*/
ctx: CanvasRenderingContext2D;
/**
* 轨迹路径
*/
path: IRibbonPoint[];
/**
* 区域宽度
*/
width: number;
/**
* 区域高度
*/
height: number;
/**
* 颜色色系
*/
colorIndex: number;
/**
* 配置项
*/
options: IRibbonOptions
constructor(options: IRibbonOptions = {
alpha: 0.6,
size: 300,
zIndex: 0
}) {
this.options = options
// 生成Canvas元素 并添加到Body中
this.canvas = document.createElement('canvas')
this.canvas.classList.add('ribbon-canvas')
this.canvas.style.cssText = `position:fixed;top:0;left:0;z-index:${this.options.zIndex}`
document.getElementsByTagName('body')[0].appendChild(this.canvas)
this.ctx = this.canvas.getContext('2d') as CanvasRenderingContext2D
this.width = window.innerWidth
this.height = window.innerHeight
// 返回实际宽高
const dpr = window.devicePixelRatio || 1
this.canvas.width = this.width * dpr
this.canvas.height = this.height * dpr
// 水平、竖直方向缩放
this.ctx.scale(dpr, dpr)
// 图形透明度
this.ctx.globalAlpha = this.options.alpha
this.path = []
this.colorIndex = 0
this.reDraw()
}
/**
* 重新绘制
*/
reDraw() {
// 清除之前绘制的图形
this.ctx.clearRect(0, 0, this.width, this.height)
// 生成新的点和路径
const point1: IRibbonPoint = {
x: 0,
y: this.height * 0.7 + this.options.size
}
const point2: IRibbonPoint = {
x: 0,
y: this.height * 0.7 - this.options.size
}
this.path = [point1, point2]
// 随机选择色系
this.colorIndex = getRandomInt(randomColor.length)
// 路径没有填满屏幕宽度时,绘制路径
while (this.path[1].x < this.width + this.options.size) {
this.draw(this.path[0], this.path[1])
}
}
/**
* 绘制线
* @param start 起点
* @param end 终点
*/
draw(start: IRibbonPoint, end: IRibbonPoint) {
// 绘制当前点
this.ctx.beginPath()
this.ctx.moveTo(start.x, start.y)
this.ctx.lineTo(end.x, end.y)
const nextX = this.geneX(end.x)
const nextY = this.geneY(end.y)
this.ctx.lineTo(nextX, nextY)
this.ctx.closePath()
// 随机生成颜色
const color = randomColor[this.colorIndex]
this.ctx.fillStyle = `${color[getRandomInt(color.length)]}`
// 根据当前样式填充路径
this.ctx.fill()
// 继续绘制下一个点
// 起点更新为当前终点
this.path[0] = this.path[1]
// 更新终点
this.path[1] = { x: nextX, y: nextY }
}
/**
* 生成下一个x坐标
* @param x 当前x坐标
*/
geneX(x: number) {
// 当前坐标向上平移
return x + this.options.size
}
/**
* 生成下一个y坐标
* @param y 当前y坐标
*/
geneY(y: number) {
// 当前坐标向右平移
const temp = y + (random() * 2 - 1.1) * this.options.size
return (temp > this.height || temp < 0) ? this.height : temp
}
/**
* 清除画布
*/
clear() {
this.canvas.remove()
}
}