JSFiddle - React, Tailwind, and code Playground

by tfoller

HTML

<canvas id="main_canvas" style="border: solid 1px red;" width=600 height=600></canvas>

<script type="x-shader/x-vertex" id="vs">

  varying vec2 vuv;
  
  void main() {
     
    vec4 mvPosition = modelViewMatrix * vec4(position, 1.0);
    
    gl_Position = projectionMatrix * mvPosition;
    
    vuv = uv;
  }
</script>

<script type='x-shader/x-fragment' id='fs'>

  varying vec2 vuv;

  uniform sampler2D map;
  
  uniform mat3 mapctrl;

  void main() {
  
  	vec2 muv = (mapctrl * vec3(vuv, 1.0)).xy;
  
  	vec4 clr = texture2D(map, muv);
  
  	gl_FragColor = clr;
  }
</script>

<button id="boom" style="display: block;margin-top: 10px;">boom</button>

CSS

body {
  background: #000;
}

JavaScript

//import * as THREE from 'https://alikim.com/_v1_jsm/three.module.js'
import * as THREE from 'https://alikim.com/_v1_jsm/three141.showshader.js'

const get = (id) => document.getElementById(id);

const canvas = get('main_canvas');
const [w2, h2] = [canvas.width / 2, canvas.height / 2];
const renderer = new THREE.WebGLRenderer({
  antialias: false,
  alpha: true,
  canvas,
});

const [near, far] = [0, 1];
const camera = new THREE.OrthographicCamera(-w2, w2, h2, -h2, near, far);

const render = () => {
  renderer.render(scene, camera)
};

const mtx = {
	scaleX: 1,
	scaleY: 1,
	skewX: 0,
	skewY: 0,
	offU: 0,
	offV: 0,
};

const size = 300;

const animator1 = (mtx, tile, fps) => {
	const showTile = (tile, off) => {
  	mtx.scaleX = tile[0];
    mtx.scaleY = tile[1];
    mtx.offU = off[0];
    mtx.offV = off[1];
    render();
  };
  const fr = 1000 / fps;
	const tuv = [1/tile[0], 1/tile[1]];
  const off = [];
  for(let j = 0; j < tile[1]; j++) {
  	off[1] = 1 - tuv[1] * (j + 1);
  	for(let i = 0; i < tile[0]; i++) {
  		off[0] = tuv[0] * i;
      const del = (j * tile[0] + i) * fr;
      setTimeout(showTile, del, [...tile], [...off]);
  	}
  }
};

const fps = 30;
const tile = [5, 5];
document.getElementById('boom').addEventListener('click', () => {animator1(mtx, tile, fps)});


const geo = new THREE.PlaneBufferGeometry(size, size, 1, 1);
const url = "https://i.postimg.cc/Y0Kvywgk/Explosion01-nofire-5x5-1.png";
const map = new THREE.TextureLoader().load(url, render);

const mat = new THREE.MeshBasicMaterial({
	map,
  transparent: true,
});

const mat1 = new THREE.ShaderMaterial({
  vertexShader: get('vs').textContent,
  fragmentShader: get('fs').textContent,
  transparent: true,
  uniforms: {
    map: {
      value: map
    },
    mapctrl: {
      get value() {
        const degToRad = Math.PI / 180;
        const [sX, sY, skX, skY, tX, tY] = [
          1 / mtx.scaleX || 1, 1 / mtx.scaleY || 1, degToRad * mtx.skewX || 0,
          degToRad * mtx.skewY || 0, mtx.offU ||...