async function fragment(fragmentString, options = {}) {
let {
w = width,
h = 400,
background = 'black',
play = false,
logShader = false,
resolution = 1,
u_texture
} = options
if(logShader) return fragmentString
const canvas = DOM.canvas(w * devicePixelRatio * resolution | 0, h * devicePixelRatio * resolution | 0)
if(w === width) {
w += 28
canvas.width = w * devicePixelRatio
}
canvas.style = `
width: ${w}px;
height: ${h}px;
background: ${background};
`
const gl = canvas.value = canvas.getContext('webgl')
const vertexShader = createShader(gl, gl.VERTEX_SHADER, `
precision mediump float;
attribute vec2 a_position;
uniform vec2 u_resolution;
void main() {
gl_Position = vec4((a_position / u_resolution * 2. - 1.) * vec2(1, -1), 0., 1.);
}`)
const fragmentShader = createShader(gl, gl.FRAGMENT_SHADER, fragmentString)
const program = createProgram(gl, vertexShader, fragmentShader)
const vertexBuffer = createVertexBuffer(gl)
const a_positionLoc = gl.getAttribLocation(program, 'a_position')
const u_timeLoc = gl.getUniformLocation(program, 'u_time')
const u_resolutionLoc = gl.getUniformLocation(program, 'u_resolution')
const u_mouseLoc = gl.getUniformLocation(program, 'u_mouse')
if (u_texture) u_texture = await loadTexture(gl, u_texture)
canvas.addEventListener('mousemove', e => {
gl.uniform2f(u_mouseLoc, e.offsetX * devicePixelRatio * resolution | 0, (h - e.offsetY) * devicePixelRatio * resolution | 0)
})
let startTime = Date.now()
let pauseTimestamp = Date.now()
canvas.addEventListener('click', e => {
play = !play
if(!play) pauseTimestamp = Date.now()
else {
startTime += Date.now() - pauseTimestamp
}
})
let isAnimated = (RegExp(`\\bu_time`, 'g')).test(fragmentString) || (RegExp(`\\bu_mouse`, 'g')).test(fragmentString)
let firstFrame = true;
function* rendering() {
while(isAnimated || firstFrame) {
firstFrame = false;
gl.viewport(0, 0, gl.canvas.width, gl.canvas.height)
gl.useProgram(program)
const time = (Date.now() - startTime) / 1000
gl.uniform1f(u_timeLoc, time)
gl.uniform2f(u_resolutionLoc, gl.canvas.width, gl.canvas.height)
if (u_texture) gl.bindTexture(gl.TEXTURE_2D, u_texture)
gl.clearColor(1.0, 1.0, Math.sin(time) / 2.0 + 0.5, 1.0)
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT)
gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer)
gl.enableVertexAttribArray(a_positionLoc)
const numComponents = 2
const type = gl.FLOAT
const normalize = false
const stride = 0
const offset = 0
gl.vertexAttribPointer(a_positionLoc, numComponents, type, normalize, stride, offset)
const primitiveType = gl.TRIANGLE_STRIP
const count = 4
gl.drawArrays(primitiveType, offset, count)
yield canvas
while(!play) yield canvas
}
}
function isScrolledIntoView(el) {
var rect = el.getBoundingClientRect();
var elemTop = rect.top;
var elemBottom = rect.bottom;
var isVisible = (elemTop >= 0) && (elemBottom <= window.innerHeight);
return isVisible;
}
function isInViewport(element) {
const rect = element.getBoundingClientRect();
return (
rect.top >= 0 &&
rect.left >= 0 &&
rect.bottom <= (window.innerHeight || document.documentElement.clientHeight) &&
rect.right <= (window.innerWidth || document.documentElement.clientWidth)
);
}
return rendering()
}