152 lines
4.6 KiB
JavaScript
152 lines
4.6 KiB
JavaScript
console.clear();
|
|
|
|
class DigitalArt extends HTMLElement {
|
|
|
|
constructor() {
|
|
super();
|
|
this.canvas = null;
|
|
this.gl = null;
|
|
this.onResize = this.onResize.bind(this);
|
|
this.loop = this.loop.bind(this);
|
|
}
|
|
|
|
static register() {
|
|
customElements.define("digital-art", DigitalArt);
|
|
}
|
|
|
|
connectedCallback() {
|
|
if (! this.gl) {
|
|
this.setup();
|
|
}
|
|
}
|
|
|
|
disconnectedCallback() {
|
|
this.dispose();
|
|
}
|
|
|
|
get devicePixelRatio() {
|
|
return parseFloat(this.getAttribute('dpr')) || window.devicePixelRatio;
|
|
}
|
|
|
|
|
|
onResize() {
|
|
const { canvas, gl, program } = this;
|
|
const width = this.clientWidth;
|
|
const height = this.clientHeight;
|
|
const dpr = this.devicePixelRatio;
|
|
canvas.width = width * dpr;
|
|
canvas.height = height * dpr;
|
|
gl.viewport(0, 0, gl.drawingBufferWidth, gl.drawingBufferHeight);
|
|
const uResolution = gl.getUniformLocation(program, "resolution");
|
|
gl.uniform2fv(uResolution, [gl.drawingBufferWidth, gl.drawingBufferHeight]);
|
|
}
|
|
|
|
createShader(type, code) {
|
|
const { gl } = this;
|
|
const sh = gl.createShader(type, code);
|
|
gl.shaderSource(sh, code);
|
|
gl.compileShader(sh);
|
|
if (!gl.getShaderParameter(sh, gl.COMPILE_STATUS)) {
|
|
throw gl.getShaderInfoLog(sh);
|
|
}
|
|
return sh;
|
|
}
|
|
|
|
createBuffers() {
|
|
const { gl, program } = this;
|
|
const bufferScripts = [...this.querySelectorAll('[type=buffer]')];
|
|
this.buffers = {};
|
|
let count = -1;
|
|
bufferScripts.forEach(container => {
|
|
const name = container.getAttribute('name') || 'position';
|
|
const recordSize = parseInt(container.getAttribute('data-size'), 10) || 1;
|
|
const data = new Float32Array(JSON.parse(container.textContent.trim()));
|
|
const buffer = gl.createBuffer();
|
|
gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
|
|
gl.bufferData(
|
|
gl.ARRAY_BUFFER,
|
|
data,
|
|
gl.STATIC_DRAW
|
|
);
|
|
const attribLoc = gl.getAttribLocation(program, name);
|
|
this.buffers[name] = { buffer, data, attribLoc, recordSize };
|
|
count = Math.max(count, (data.length / recordSize)|0);
|
|
gl.enableVertexAttribArray(attribLoc);
|
|
gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
|
|
gl.vertexAttribPointer(attribLoc, recordSize, gl.FLOAT, false, 0, 0);
|
|
});
|
|
this.count = count;
|
|
}
|
|
|
|
loop(time = 0) {
|
|
const { gl, program } = this;
|
|
const uTime = gl.getUniformLocation(program, "time");
|
|
gl.uniform1f(uTime, time);
|
|
gl.drawArrays(gl.TRIANGLES, 0, this.count);
|
|
this.frame = requestAnimationFrame(this.loop);
|
|
}
|
|
|
|
createPrograms() {
|
|
const { gl } = this;
|
|
const fragScript = this.querySelector('[type=frag]');
|
|
const vertScript = this.querySelector('[type=vert]');
|
|
const HEADER = 'precision highp float;';
|
|
const DEFAULT_VERT = HEADER + 'attribute vec4 position;void main(){gl_Position=position;}';
|
|
const DEFAULT_FRAG = HEADER + 'void main(){gl_FragColor=vec4(1.,0,0,1.);}';
|
|
|
|
this.fragCode = fragScript?.textContent || DEFAULT_FRAG;
|
|
this.vertCode = vertScript?.textContent || DEFAULT_VERT;
|
|
|
|
const program = gl.createProgram();
|
|
this.program = program;
|
|
this.gl = gl;
|
|
|
|
this.fragShader = this.createShader(gl.FRAGMENT_SHADER, this.fragCode);
|
|
this.vertShader = this.createShader(gl.VERTEX_SHADER, this.vertCode);
|
|
|
|
gl.attachShader(program, this.fragShader);
|
|
gl.attachShader(program, this.vertShader);
|
|
gl.linkProgram(program);
|
|
if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {
|
|
throw gl.getProgramInfoLog(program);
|
|
}
|
|
}
|
|
|
|
setup() {
|
|
this.canvas = document.createElement('canvas');
|
|
this.dpr = window.devicePixelRatio;
|
|
this.appendChild(this.canvas);
|
|
this.gl = this.canvas.getContext('webgl') || this.canvas.getContext('experimental-webgl');
|
|
this.createPrograms();
|
|
|
|
const { program, gl } = this;
|
|
|
|
gl.useProgram(program);
|
|
this.createBuffers();
|
|
gl.clearColor(0, 0, 0, 1);
|
|
gl.clear(gl.COLOR_BUFFER_BIT);
|
|
this.onResize();
|
|
window.addEventListener('resize', this.onResize, false);
|
|
this.frame = requestAnimationFrame(this.loop);
|
|
}
|
|
|
|
dispose() {
|
|
cancelAnimationFrame(this.loop);
|
|
this.frame = -1;
|
|
window.removeEventListener('resize', this.onResize, false);
|
|
Object.entries(this.buffers).forEach(([name, buf]) => {
|
|
this.gl.deleteBuffer(buf.buffer);
|
|
});
|
|
this.gl.deleteProgram(this.program);
|
|
const loseCtx = this.gl.getExtension('WEBGL_lose_context');
|
|
if (loseCtx && typeof loseCtx.loseContext === 'function') {
|
|
loseCtx.loseContext();
|
|
}
|
|
this.removeChild(this.canvas);
|
|
this.gl = null;
|
|
this.canvas = null;
|
|
this.buffers = {}
|
|
}
|
|
}
|
|
|
|
DigitalArt.register(); |