165 lines
3.6 KiB
TypeScript
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()
|
|
}
|
|
}
|
|
|