From ea91baf8e1679fb0a984a60472e37ea47f3c3d44 Mon Sep 17 00:00:00 2001 From: Vitalij Ryndin Date: Thu, 21 Sep 2023 21:21:57 +0800 Subject: [PATCH 1/4] feat: add `@twallpaper/webgl` --- packages/twallpaper-webgl/README.md | 38 +++ packages/twallpaper-webgl/package.json | 50 +++ packages/twallpaper-webgl/src/distance.ts | 3 + .../twallpaper-webgl/src/fragment-shader.glsl | 37 +++ packages/twallpaper-webgl/src/hex-to-vec3.ts | 15 + packages/twallpaper-webgl/src/index.ts | 8 + packages/twallpaper-webgl/src/load-shaders.ts | 22 ++ packages/twallpaper-webgl/src/style.css | 45 +++ packages/twallpaper-webgl/src/twallpaper.ts | 289 ++++++++++++++++++ .../twallpaper-webgl/src/vertex-shader.glsl | 5 + packages/twallpaper-webgl/src/vite-env.d.ts | 1 + packages/twallpaper-webgl/tsconfig.json | 11 + packages/twallpaper-webgl/vite.config.ts | 38 +++ pnpm-lock.yaml | 9 + 14 files changed, 571 insertions(+) create mode 100644 packages/twallpaper-webgl/README.md create mode 100644 packages/twallpaper-webgl/package.json create mode 100644 packages/twallpaper-webgl/src/distance.ts create mode 100644 packages/twallpaper-webgl/src/fragment-shader.glsl create mode 100644 packages/twallpaper-webgl/src/hex-to-vec3.ts create mode 100644 packages/twallpaper-webgl/src/index.ts create mode 100644 packages/twallpaper-webgl/src/load-shaders.ts create mode 100644 packages/twallpaper-webgl/src/style.css create mode 100644 packages/twallpaper-webgl/src/twallpaper.ts create mode 100644 packages/twallpaper-webgl/src/vertex-shader.glsl create mode 100644 packages/twallpaper-webgl/src/vite-env.d.ts create mode 100644 packages/twallpaper-webgl/tsconfig.json create mode 100644 packages/twallpaper-webgl/vite.config.ts diff --git a/packages/twallpaper-webgl/README.md b/packages/twallpaper-webgl/README.md new file mode 100644 index 0000000..c52a47e --- /dev/null +++ b/packages/twallpaper-webgl/README.md @@ -0,0 +1,38 @@ +
+

+ + +
+

TWallpaper

+ +

+ +

+ 🌈 Multicolor gradient wallpaper created algorithmically and shimmers smoothly. +

+ +

+ + npm + + + npm + + + npm bundle size + +

+ +## Installation + +```sh +npm install @twallpaper/webgl +``` + +```sh +yarn add @twallpaper/webgl +``` + +```sh +pnpm add @twallpaper/webgl +``` diff --git a/packages/twallpaper-webgl/package.json b/packages/twallpaper-webgl/package.json new file mode 100644 index 0000000..89af6f4 --- /dev/null +++ b/packages/twallpaper-webgl/package.json @@ -0,0 +1,50 @@ +{ + "name": "@twallpaper/webgl", + "version": "2.1.2", + "description": "🌈 Multicolor gradient wallpaper created algorithmically and shimmers smoothly.", + "type": "module", + "types": "./dist/index.d.ts", + "main": "./dist/index.cjs.js", + "jsdelivr": "./dist/index.umd.js", + "unpkg": "./dist/index.umd.js", + "module": "./dist/index.es.js", + "exports": { + ".": { + "require": "./dist/index.cjs.js", + "import": "./dist/index.es.js" + }, + "./dist/style.css": "./dist/style.css", + "./css": "./dist/style.css" + }, + "files": [ + "dist" + ], + "license": "MIT", + "repository": { + "type": "git", + "url": "git+https://github.com/crashmax-dev/twallpaper.git" + }, + "bugs": { + "url": "https://github.com/crashmax-dev/twallpaper/issues" + }, + "homepage": "https://twallpaper.js.org", + "keywords": [ + "react", + "multicolor", + "gradient", + "wallpaper", + "background", + "animation", + "telegram", + "canvas", + "webgl" + ], + "scripts": { + "dev": "vite build --watch", + "build": "vite build" + }, + "devDependencies": { + "typescript": "^5.0.4", + "vite": "^4.3.1" + } +} diff --git a/packages/twallpaper-webgl/src/distance.ts b/packages/twallpaper-webgl/src/distance.ts new file mode 100644 index 0000000..1389e6c --- /dev/null +++ b/packages/twallpaper-webgl/src/distance.ts @@ -0,0 +1,3 @@ +export function distance(a: number[], b: number[]): number { + return Math.sqrt((a[1] - b[1]) * (a[1] - b[1])) +} diff --git a/packages/twallpaper-webgl/src/fragment-shader.glsl b/packages/twallpaper-webgl/src/fragment-shader.glsl new file mode 100644 index 0000000..1476061 --- /dev/null +++ b/packages/twallpaper-webgl/src/fragment-shader.glsl @@ -0,0 +1,37 @@ +precision highp float; + +uniform vec2 resolution; + +uniform vec3 color1; +uniform vec3 color2; +uniform vec3 color3; +uniform vec3 color4; + +uniform vec2 color1Pos; +uniform vec2 color2Pos; +uniform vec2 color3Pos; +uniform vec2 color4Pos; + +void main() { + vec2 position = gl_FragCoord.xy / resolution.xy; + position.y = 1.0 - position.y; + + float dp1 = distance(position, color1Pos); + float dp2 = distance(position, color2Pos); + float dp3 = distance(position, color3Pos); + float dp4 = distance(position, color4Pos); + float minD = min(dp1, min(dp2, min(dp3, dp4))); + float p = 3.0; + + dp1 = pow(1.0 - (dp1 - minD), p); + dp2 = pow(1.0 - (dp2 - minD), p); + dp3 = pow(1.0 - (dp3 - minD), p); + dp4 = pow(1.0 - (dp4 - minD), p); + float dpt = abs(dp1 + dp2 + dp3 + dp4); + + gl_FragColor = + (vec4(color1 / 255.0, 1.0) * dp1 / dpt) + + (vec4(color2 / 255.0, 1.0) * dp2 / dpt) + + (vec4(color3 / 255.0, 1.0) * dp3 / dpt) + + (vec4(color4 / 255.0, 1.0) * dp4 / dpt); +} diff --git a/packages/twallpaper-webgl/src/hex-to-vec3.ts b/packages/twallpaper-webgl/src/hex-to-vec3.ts new file mode 100644 index 0000000..59bf19f --- /dev/null +++ b/packages/twallpaper-webgl/src/hex-to-vec3.ts @@ -0,0 +1,15 @@ +export type Vec3 = [r: number, g: number, b: number]; + +export function hexToVec3( + hex: string +): Vec3 { + if (hex.startsWith('#')) { + hex = hex.slice(1) + } + + const r = parseInt(hex.slice(0, 2), 16) + const g = parseInt(hex.slice(2, 4), 16) + const b = parseInt(hex.slice(4, 6), 16) + + return [r, g, b] +} diff --git a/packages/twallpaper-webgl/src/index.ts b/packages/twallpaper-webgl/src/index.ts new file mode 100644 index 0000000..84dc644 --- /dev/null +++ b/packages/twallpaper-webgl/src/index.ts @@ -0,0 +1,8 @@ +import { Twallpaper } from './twallpaper.js' +export { hexToVec3 } from './hex-to-vec3.js' + +export type { TwallpaperOptions } from './twallpaper.js' +export type { Vec3 } from './hex-to-vec3.js' + +export { Twallpaper } +export default Twallpaper diff --git a/packages/twallpaper-webgl/src/load-shaders.ts b/packages/twallpaper-webgl/src/load-shaders.ts new file mode 100644 index 0000000..774e9a5 --- /dev/null +++ b/packages/twallpaper-webgl/src/load-shaders.ts @@ -0,0 +1,22 @@ +export function loadShaders( + gl: WebGLRenderingContext, + shaderSources: [vertexShader: string, fragmentShader: string], +): readonly [WebGLShader, WebGLShader] { + const [vertexShader, fragmentShader] = shaderSources; + return [ + createShader(gl, vertexShader, gl.VERTEX_SHADER), + createShader(gl, fragmentShader, gl.FRAGMENT_SHADER), + ] as const; +} + +function createShader( + gl: WebGLRenderingContext, + shaderSource: string, + shaderType: number, +): WebGLShader { + const shader = gl.createShader(shaderType)!; + gl.shaderSource(shader, shaderSource); + gl.compileShader(shader); + gl.getShaderParameter(shader, gl.COMPILE_STATUS); + return shader; +} diff --git a/packages/twallpaper-webgl/src/style.css b/packages/twallpaper-webgl/src/style.css new file mode 100644 index 0000000..aff435a --- /dev/null +++ b/packages/twallpaper-webgl/src/style.css @@ -0,0 +1,45 @@ +.tw-mask { + -webkit-mask: center repeat; + -webkit-mask-size: var(--tw-size) auto; + -webkit-mask-image: var(--tw-image); + opacity: var(--tw-opacity); +} + +.tw-wrap { + position: fixed; + left: 0; + top: 0; + width: 100%; + height: 100%; + pointer-events: none; + z-index: -1; + background: var(--tw-background); +} + +.tw-canvas.tw-mask + .tw-pattern { + background-image: none; + opacity: initial; + filter: none; +} + +.tw-canvas { + top: 0; + left: 0; + width: 100%; + height: 100%; + position: absolute; +} + +.tw-pattern { + position: absolute; + left: 0; + top: 0; + width: 100%; + height: 100%; + mix-blend-mode: soft-light; + background: center repeat; + background-size: var(--tw-size) auto; + background-image: var(--tw-image); + filter: blur(var(--tw-blur)); + opacity: var(--tw-opacity); +} diff --git a/packages/twallpaper-webgl/src/twallpaper.ts b/packages/twallpaper-webgl/src/twallpaper.ts new file mode 100644 index 0000000..a7a6cad --- /dev/null +++ b/packages/twallpaper-webgl/src/twallpaper.ts @@ -0,0 +1,289 @@ +import { distance } from './distance.js' +import fragmentShader from './fragment-shader.glsl?raw' +import { loadShaders } from './load-shaders.js' +import vertexShader from './vertex-shader.glsl?raw' +import type { Vec3 } from './hex-to-vec3.js' + +const KEY_POINTS = [ + [0.265, 0.582], // 0 + [0.176, 0.918], // 1 + [1 - 0.585, 1 - 0.164], // 0 + [0.644, 0.755], // 1 + [1 - 0.265, 1 - 0.582], // 0 + [1 - 0.176, 1 - 0.918], // 1 + [0.585, 0.164], // 0 + [1 - 0.644, 1 - 0.755] // 1 + +] + +export interface TwallpaperOptions { + colors: Vec3[] + mask: boolean + image: string + backgroundColor: string + size: number + opacity: number +} + +export class Twallpaper { + private gl: WebGLRenderingContext + private glslProgram: WebGLProgram + + private options: TwallpaperOptions + private gradientContainer: HTMLCanvasElement + private maskContainer: HTMLDivElement + + private resolutionLoc: WebGLUniformLocation + private color1Loc: WebGLUniformLocation + private color2Loc: WebGLUniformLocation + private color3Loc: WebGLUniformLocation + private color4Loc: WebGLUniformLocation + private color1PosLoc: WebGLUniformLocation + private color2PosLoc: WebGLUniformLocation + private color3PosLoc: WebGLUniformLocation + private color4PosLoc: WebGLUniformLocation + + private targetColor1Pos: number[] + private targetColor2Pos: number[] + private targetColor3Pos: number[] + private targetColor4Pos: number[] + + private color1Pos: number[] + private color2Pos: number[] + private color3Pos: number[] + private color4Pos: number[] + + private keyShift = 0 + private speed = 0.1 + private animating = false + + constructor(private readonly container: HTMLElement) { + this.render = this.render.bind(this) + } + + updateOptions(options: Partial): void { + this.options = { ...this.options, ...options } + } + + init(options: TwallpaperOptions): void { + this.updateOptions(options) + + this.gradientContainer = document.createElement('canvas') + this.gradientContainer.classList.add('wallpaper-canvas') + + this.maskContainer = document.createElement('div') + this.maskContainer.classList.add('wallpaper-pattern') + + this.container.classList.add('wallpaper-wrap') + this.container.append(this.gradientContainer, this.maskContainer) + + this.updateMask() + + this.gl = this.gradientContainer.getContext('webgl')! + if (!this.gl) { + throw new Error('WebGL not supported') + } + + this.glslProgram = this.gl.createProgram()! + if (!this.glslProgram) { + throw new Error('Unable to create WebGLProgram') + } + + const shaders = loadShaders(this.gl, [vertexShader, fragmentShader]) + for (const shader of shaders) { + this.gl.attachShader(this.glslProgram, shader) + } + + this.gl.linkProgram(this.glslProgram) + + if (!this.gl.getProgramParameter(this.glslProgram, this.gl.LINK_STATUS)) { + throw new Error('Unable to initialize the shader program.') + } + + this.gl.useProgram(this.glslProgram) + + // look up where the vertex data needs to go. + const positionAttributeLocation = this.gl.getAttribLocation( + this.glslProgram, + 'a_position' + ) + + // Create a buffer to put three 2d clip space points in + const positionBuffer = this.gl.createBuffer() + + // Bind it to ARRAY_BUFFER (think of it as ARRAY_BUFFER = positionBuffer) + this.gl.bindBuffer(this.gl.ARRAY_BUFFER, positionBuffer) + + // fill it with a 2 triangles that cover clipspace + this.gl.bufferData( + this.gl.ARRAY_BUFFER, + new Float32Array([ + -1, + -1, // first triangle + 1, + -1, + -1, + 1, + -1, + 1, // second triangle + 1, + -1, + 1, + 1 + + ]), + this.gl.STATIC_DRAW + ) + + // Tell WebGL how to convert from clip space to pixels + this.gl.viewport(0, 0, this.gl.canvas.width, this.gl.canvas.height) + + // Tell it to use our program (pair of shaders) + this.gl.useProgram(this.glslProgram) + + // Turn on the attribute + this.gl.enableVertexAttribArray(positionAttributeLocation) + + // Bind the position buffer. + this.gl.bindBuffer(this.gl.ARRAY_BUFFER, positionBuffer) + + // Tell the attribute how to get data out of positionBuffer (ARRAY_BUFFER) + this.gl.vertexAttribPointer( + positionAttributeLocation, + 2, // 2 components per iteration + this.gl.FLOAT, // the data is 32bit floats + false, // don't normalize the data + 0, // 0 = move forward size * sizeof(type) each iteration to get the next position + 0 // start at the beginning of the buffer + ) + + this.resolutionLoc = this.gl.getUniformLocation( + this.glslProgram, + 'resolution' + )! + this.color1Loc = this.gl.getUniformLocation(this.glslProgram, 'color1')! + this.color2Loc = this.gl.getUniformLocation(this.glslProgram, 'color2')! + this.color3Loc = this.gl.getUniformLocation(this.glslProgram, 'color3')! + this.color4Loc = this.gl.getUniformLocation(this.glslProgram, 'color4')! + this.color1PosLoc = this.gl.getUniformLocation( + this.glslProgram, + 'color1Pos' + )! + this.color2PosLoc = this.gl.getUniformLocation( + this.glslProgram, + 'color2Pos' + )! + this.color3PosLoc = this.gl.getUniformLocation( + this.glslProgram, + 'color3Pos' + )! + this.color4PosLoc = this.gl.getUniformLocation( + this.glslProgram, + 'color4Pos' + )! + + this.updateColors() + + this.color1Pos = [this.targetColor1Pos![0], this.targetColor1Pos![1]]! + this.color2Pos = [this.targetColor2Pos![0], this.targetColor2Pos![1]]! + this.color3Pos = [this.targetColor3Pos![0], this.targetColor3Pos![1]]! + this.color4Pos = [this.targetColor4Pos![0], this.targetColor4Pos![1]]! + + this.renderGradient() + } + + updateMask(): void { + const { image, mask, opacity, size, backgroundColor } = this.options + + this.container.style.setProperty('--wallpaper-opacity', `${opacity}`) + this.container.style.setProperty('--wallpaper-size', `${size}px`) + this.container.style.setProperty( + '--wallpaper-background-color', + backgroundColor + ) + + this.container.style.setProperty('--wallpaper-image', `url(${image})`) + + if (mask) { + this.gradientContainer.classList.add('wallpaper-mask') + } else { + this.gradientContainer.classList.remove('wallpaper-mask') + } + } + + renderGradient(): void { + this.gl.uniform2fv(this.resolutionLoc, [ + this.gl.canvas.width, + this.gl.canvas.height + ]) + this.gl.uniform3fv(this.color1Loc, this.options.colors[0]) + this.gl.uniform3fv(this.color2Loc, this.options.colors[1]) + this.gl.uniform3fv(this.color3Loc, this.options.colors[2]) + this.gl.uniform3fv(this.color4Loc, this.options.colors[3]) + this.gl.uniform2fv(this.color1PosLoc, this.color1Pos) + this.gl.uniform2fv(this.color2PosLoc, this.color2Pos) + this.gl.uniform2fv(this.color3PosLoc, this.color3Pos) + this.gl.uniform2fv(this.color4PosLoc, this.color4Pos) + + this.gl.drawArrays( + this.gl.TRIANGLES, + 0, // offset + 6 // num vertices to process + ) + } + + animate(): void { + this.updateColors() + if (!this.animating) { + requestAnimationFrame(this.render) + } + } + + private updateColors(): void { + this.targetColor1Pos = KEY_POINTS[this.keyShift % 8] + this.targetColor2Pos = KEY_POINTS[(this.keyShift + 2) % 8] + this.targetColor3Pos = KEY_POINTS[(this.keyShift + 4) % 8] + this.targetColor4Pos = KEY_POINTS[(this.keyShift + 6) % 8] + this.keyShift = (this.keyShift + 1) % 8 + } + + private render(): void { + this.animating = true + + if ( + distance(this.color1Pos, this.targetColor1Pos) > 0.01 || + distance(this.color2Pos, this.targetColor2Pos) > 0.01 || + distance(this.color3Pos, this.targetColor3Pos) > 0.01 || + distance(this.color3Pos, this.targetColor3Pos) > 0.01 + ) { + this.color1Pos[0] = + this.color1Pos[0] * (1 - this.speed) + + this.targetColor1Pos[0] * this.speed + this.color1Pos[1] = + this.color1Pos[1] * (1 - this.speed) + + this.targetColor1Pos[1] * this.speed + this.color2Pos[0] = + this.color2Pos[0] * (1 - this.speed) + + this.targetColor2Pos[0] * this.speed + this.color2Pos[1] = + this.color2Pos[1] * (1 - this.speed) + + this.targetColor2Pos[1] * this.speed + this.color3Pos[0] = + this.color3Pos[0] * (1 - this.speed) + + this.targetColor3Pos[0] * this.speed + this.color3Pos[1] = + this.color3Pos[1] * (1 - this.speed) + + this.targetColor3Pos[1] * this.speed + this.color4Pos[0] = + this.color4Pos[0] * (1 - this.speed) + + this.targetColor4Pos[0] * this.speed + this.color4Pos[1] = + this.color4Pos[1] * (1 - this.speed) + + this.targetColor4Pos[1] * this.speed + this.renderGradient() + requestAnimationFrame(this.render) + } else { + this.animating = false + } + } +} diff --git a/packages/twallpaper-webgl/src/vertex-shader.glsl b/packages/twallpaper-webgl/src/vertex-shader.glsl new file mode 100644 index 0000000..69b88d8 --- /dev/null +++ b/packages/twallpaper-webgl/src/vertex-shader.glsl @@ -0,0 +1,5 @@ +attribute vec4 a_position; + +void main() { + gl_Position = a_position; +} diff --git a/packages/twallpaper-webgl/src/vite-env.d.ts b/packages/twallpaper-webgl/src/vite-env.d.ts new file mode 100644 index 0000000..11f02fe --- /dev/null +++ b/packages/twallpaper-webgl/src/vite-env.d.ts @@ -0,0 +1 @@ +/// diff --git a/packages/twallpaper-webgl/tsconfig.json b/packages/twallpaper-webgl/tsconfig.json new file mode 100644 index 0000000..e3b4b5d --- /dev/null +++ b/packages/twallpaper-webgl/tsconfig.json @@ -0,0 +1,11 @@ +{ + "extends": "@crashmax/tsconfig", + "compilerOptions": { + "moduleResolution": "node", + "strictNullChecks": false, + "outDir": "dist" + }, + "include": [ + "src/**/*" + ] +} diff --git a/packages/twallpaper-webgl/vite.config.ts b/packages/twallpaper-webgl/vite.config.ts new file mode 100644 index 0000000..5ff05e8 --- /dev/null +++ b/packages/twallpaper-webgl/vite.config.ts @@ -0,0 +1,38 @@ +import { copyFile } from 'node:fs/promises' +import { resolve } from 'node:path' +import { defineConfig } from 'vite' +import dts from 'vite-plugin-dts' + +export default defineConfig({ + plugins: [ + dts({ + insertTypesEntry: true, + async afterBuild() { + try { + await copyFile('src/style.css', 'dist/style.css') + } catch (err) { + console.log(err) + process.exit(1) + } + } + }) + ], + build: { + target: 'esnext', + lib: { + entry: resolve(__dirname, 'src/index.ts'), + name: 'TWallpaperWebGL', + formats: [ + 'cjs', + 'es', + 'umd' + ], + fileName: (format) => `index.${format}.js` + }, + rollupOptions: { + output: { + exports: 'named' + } + } + } +}) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index e430f44..0762c5d 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -150,6 +150,15 @@ importers: specifier: ^4.3.1 version: 4.3.1(@types/node@18.16.0) + packages/twallpaper-webgl: + devDependencies: + typescript: + specifier: ^5.0.4 + version: 5.0.4 + vite: + specifier: ^4.3.1 + version: 4.3.1(@types/node@18.16.0) + packages/vue: dependencies: twallpaper: From 99bb9480b01d4df132b10d8118d50a0ea720ce87 Mon Sep 17 00:00:00 2001 From: Vitalij Ryndin Date: Thu, 21 Sep 2023 21:48:15 +0800 Subject: [PATCH 2/4] chore: rename to TWallpaperWebGL --- packages/twallpaper-webgl/src/hex-to-vec3.ts | 12 ++++--- packages/twallpaper-webgl/src/index.ts | 9 +++--- packages/twallpaper-webgl/src/load-shaders.ts | 19 ++++++------ packages/twallpaper-webgl/src/style.css | 2 +- packages/twallpaper-webgl/src/twallpaper.ts | 31 +++++++++---------- 5 files changed, 36 insertions(+), 37 deletions(-) diff --git a/packages/twallpaper-webgl/src/hex-to-vec3.ts b/packages/twallpaper-webgl/src/hex-to-vec3.ts index 59bf19f..a2cc5db 100644 --- a/packages/twallpaper-webgl/src/hex-to-vec3.ts +++ b/packages/twallpaper-webgl/src/hex-to-vec3.ts @@ -1,8 +1,6 @@ -export type Vec3 = [r: number, g: number, b: number]; +export type Vec3 = [r: number, g: number, b: number] -export function hexToVec3( - hex: string -): Vec3 { +export function hexToVec3(hex: string): Vec3 { if (hex.startsWith('#')) { hex = hex.slice(1) } @@ -11,5 +9,9 @@ export function hexToVec3( const g = parseInt(hex.slice(2, 4), 16) const b = parseInt(hex.slice(4, 6), 16) - return [r, g, b] + return [ + r, + g, + b + ] } diff --git a/packages/twallpaper-webgl/src/index.ts b/packages/twallpaper-webgl/src/index.ts index 84dc644..455bb63 100644 --- a/packages/twallpaper-webgl/src/index.ts +++ b/packages/twallpaper-webgl/src/index.ts @@ -1,8 +1,9 @@ -import { Twallpaper } from './twallpaper.js' +import { TWallpaperWebGL } from './twallpaper.js' + export { hexToVec3 } from './hex-to-vec3.js' -export type { TwallpaperOptions } from './twallpaper.js' +export type { TWallpaperWebGLOptions } from './twallpaper.js' export type { Vec3 } from './hex-to-vec3.js' -export { Twallpaper } -export default Twallpaper +export { TWallpaperWebGL } +export default TWallpaperWebGL diff --git a/packages/twallpaper-webgl/src/load-shaders.ts b/packages/twallpaper-webgl/src/load-shaders.ts index 774e9a5..9be6379 100644 --- a/packages/twallpaper-webgl/src/load-shaders.ts +++ b/packages/twallpaper-webgl/src/load-shaders.ts @@ -1,22 +1,21 @@ export function loadShaders( gl: WebGLRenderingContext, - shaderSources: [vertexShader: string, fragmentShader: string], + shaderSources: [vertexShader: string, fragmentShader: string] ): readonly [WebGLShader, WebGLShader] { - const [vertexShader, fragmentShader] = shaderSources; + const [vertexShader, fragmentShader] = shaderSources return [ createShader(gl, vertexShader, gl.VERTEX_SHADER), - createShader(gl, fragmentShader, gl.FRAGMENT_SHADER), - ] as const; + createShader(gl, fragmentShader, gl.FRAGMENT_SHADER)] as const } function createShader( gl: WebGLRenderingContext, shaderSource: string, - shaderType: number, + shaderType: number ): WebGLShader { - const shader = gl.createShader(shaderType)!; - gl.shaderSource(shader, shaderSource); - gl.compileShader(shader); - gl.getShaderParameter(shader, gl.COMPILE_STATUS); - return shader; + const shader = gl.createShader(shaderType)! + gl.shaderSource(shader, shaderSource) + gl.compileShader(shader) + gl.getShaderParameter(shader, gl.COMPILE_STATUS) + return shader } diff --git a/packages/twallpaper-webgl/src/style.css b/packages/twallpaper-webgl/src/style.css index aff435a..5d950be 100644 --- a/packages/twallpaper-webgl/src/style.css +++ b/packages/twallpaper-webgl/src/style.css @@ -13,7 +13,7 @@ height: 100%; pointer-events: none; z-index: -1; - background: var(--tw-background); + background-color: var(--tw-background-color); } .tw-canvas.tw-mask + .tw-pattern { diff --git a/packages/twallpaper-webgl/src/twallpaper.ts b/packages/twallpaper-webgl/src/twallpaper.ts index a7a6cad..f3b3eac 100644 --- a/packages/twallpaper-webgl/src/twallpaper.ts +++ b/packages/twallpaper-webgl/src/twallpaper.ts @@ -16,7 +16,7 @@ const KEY_POINTS = [ ] -export interface TwallpaperOptions { +export interface TWallpaperWebGLOptions { colors: Vec3[] mask: boolean image: string @@ -25,11 +25,11 @@ export interface TwallpaperOptions { opacity: number } -export class Twallpaper { +export class TWallpaperWebGL { private gl: WebGLRenderingContext private glslProgram: WebGLProgram - private options: TwallpaperOptions + private options: TWallpaperWebGLOptions private gradientContainer: HTMLCanvasElement private maskContainer: HTMLDivElement @@ -61,20 +61,20 @@ export class Twallpaper { this.render = this.render.bind(this) } - updateOptions(options: Partial): void { + updateOptions(options: Partial): void { this.options = { ...this.options, ...options } } - init(options: TwallpaperOptions): void { + init(options: TWallpaperWebGLOptions): void { this.updateOptions(options) this.gradientContainer = document.createElement('canvas') - this.gradientContainer.classList.add('wallpaper-canvas') + this.gradientContainer.classList.add('tw-canvas') this.maskContainer = document.createElement('div') - this.maskContainer.classList.add('wallpaper-pattern') + this.maskContainer.classList.add('tw-pattern') - this.container.classList.add('wallpaper-wrap') + this.container.classList.add('tw-wrap') this.container.append(this.gradientContainer, this.maskContainer) this.updateMask() @@ -195,19 +195,16 @@ export class Twallpaper { updateMask(): void { const { image, mask, opacity, size, backgroundColor } = this.options - this.container.style.setProperty('--wallpaper-opacity', `${opacity}`) - this.container.style.setProperty('--wallpaper-size', `${size}px`) - this.container.style.setProperty( - '--wallpaper-background-color', - backgroundColor - ) + this.container.style.setProperty('--tw-opacity', `${opacity}`) + this.container.style.setProperty('--tw-size', `${size}px`) + this.container.style.setProperty('--tw-background-color', backgroundColor) - this.container.style.setProperty('--wallpaper-image', `url(${image})`) + this.container.style.setProperty('--tw-image', `url(${image})`) if (mask) { - this.gradientContainer.classList.add('wallpaper-mask') + this.gradientContainer.classList.add('tw-mask') } else { - this.gradientContainer.classList.remove('wallpaper-mask') + this.gradientContainer.classList.remove('tw-mask') } } From 8041bac53c0b0baf291a989df846154ec7a6fef3 Mon Sep 17 00:00:00 2001 From: Vitalij Ryndin Date: Thu, 21 Sep 2023 21:48:39 +0800 Subject: [PATCH 3/4] feat(website): webgl demo --- pnpm-lock.yaml | 3 +++ website/package.json | 1 + website/src/index.ts | 5 +++++ website/src/webgl.ts | 22 ++++++++++++++++++++++ website/webgl.html | 17 +++++++++++++++++ 5 files changed, 48 insertions(+) create mode 100644 website/src/webgl.ts create mode 100644 website/webgl.html diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 0762c5d..3399259 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -183,6 +183,9 @@ importers: website: dependencies: + '@twallpaper/webgl': + specifier: workspace:* + version: link:../packages/twallpaper-webgl '@tweakpane/core': specifier: ^1.1.8 version: 1.1.8 diff --git a/website/package.json b/website/package.json index 6e33a8e..4aa857d 100644 --- a/website/package.json +++ b/website/package.json @@ -9,6 +9,7 @@ "preview": "vite preview" }, "dependencies": { + "@twallpaper/webgl": "workspace:*", "@tweakpane/core": "^1.1.8", "lodash.clonedeep": "^4.5.0", "lodash.merge": "^4.6.2", diff --git a/website/src/index.ts b/website/src/index.ts index 3959c6f..34312da 100644 --- a/website/src/index.ts +++ b/website/src/index.ts @@ -305,6 +305,11 @@ tweakpane.addButton({ title: 'Reset' }).on('click', () => { tweakpane.addSeparator() +/** webgl version */ +tweakpane.addButton({ title: 'WebGL version' }).on('click', () => { + window.open('/webgl.html', '_blank') +}) + /** github link */ tweakpane.addButton({ title: 'GitHub' }).on('click', () => { window.open('https://github.com/crashmax-dev/twallpaper', '_blank') diff --git a/website/src/webgl.ts b/website/src/webgl.ts new file mode 100644 index 0000000..88b759f --- /dev/null +++ b/website/src/webgl.ts @@ -0,0 +1,22 @@ +import { hexToVec3, TWallpaperWebGL } from '@twallpaper/webgl' +import '@twallpaper/webgl/css' + +const container = document.querySelector('#app')! +const twallpaper = new TWallpaperWebGL(container) + +twallpaper.init({ + colors: [ + hexToVec3('#4f5bd5'), + hexToVec3('#962fbf'), + hexToVec3('#dd6cb9'), + hexToVec3('#fec496')], + backgroundColor: '#000000', + image: `${location.origin}/patterns/magic.svg`, + mask: false, + opacity: 0.3, + size: 420 +}) + +document.addEventListener('click', () => { + twallpaper.animate() +}) diff --git a/website/webgl.html b/website/webgl.html new file mode 100644 index 0000000..bbc17d6 --- /dev/null +++ b/website/webgl.html @@ -0,0 +1,17 @@ + + + + + + + + Telegram Wallpaper (WebGL) + + + + +
+ + + + From c7fb292b8d1d211e090adebde2dc50a9516980f1 Mon Sep 17 00:00:00 2001 From: Vitalij Ryndin Date: Thu, 21 Sep 2023 21:50:57 +0800 Subject: [PATCH 4/4] feat: `@twallpaper/webgl` 2.2.0 --- packages/twallpaper-webgl/CHANGELOG.md | 7 +++++++ packages/twallpaper-webgl/package.json | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) create mode 100644 packages/twallpaper-webgl/CHANGELOG.md diff --git a/packages/twallpaper-webgl/CHANGELOG.md b/packages/twallpaper-webgl/CHANGELOG.md new file mode 100644 index 0000000..954b8b5 --- /dev/null +++ b/packages/twallpaper-webgl/CHANGELOG.md @@ -0,0 +1,7 @@ +# @twallpaper/webgl + +## 2.2.0 + +### Minor Changes + +- feat: add webgl implementation diff --git a/packages/twallpaper-webgl/package.json b/packages/twallpaper-webgl/package.json index 89af6f4..fb5d6bf 100644 --- a/packages/twallpaper-webgl/package.json +++ b/packages/twallpaper-webgl/package.json @@ -1,6 +1,6 @@ { "name": "@twallpaper/webgl", - "version": "2.1.2", + "version": "2.2.0", "description": "🌈 Multicolor gradient wallpaper created algorithmically and shimmers smoothly.", "type": "module", "types": "./dist/index.d.ts",