UNPKG

shader-doodle

Version:

A friendly web-component for writing and rendering shaders.

416 lines (362 loc) 13.3 kB
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; } function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function"); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, writable: true, configurable: true } }); if (superClass) _setPrototypeOf(subClass, superClass); } function _getPrototypeOf(o) { _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) { return o.__proto__ || Object.getPrototypeOf(o); }; return _getPrototypeOf(o); } function _setPrototypeOf(o, p) { _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) { o.__proto__ = p; return o; }; return _setPrototypeOf(o, p); } function isNativeReflectConstruct() { if (typeof Reflect === "undefined" || !Reflect.construct) return false; if (Reflect.construct.sham) return false; if (typeof Proxy === "function") return true; try { Date.prototype.toString.call(Reflect.construct(Date, [], function () {})); return true; } catch (e) { return false; } } function _construct(Parent, args, Class) { if (isNativeReflectConstruct()) { _construct = Reflect.construct; } else { _construct = function _construct(Parent, args, Class) { var a = [null]; a.push.apply(a, args); var Constructor = Function.bind.apply(Parent, a); var instance = new Constructor(); if (Class) _setPrototypeOf(instance, Class.prototype); return instance; }; } return _construct.apply(null, arguments); } function _isNativeFunction(fn) { return Function.toString.call(fn).indexOf("[native code]") !== -1; } function _wrapNativeSuper(Class) { var _cache = typeof Map === "function" ? new Map() : undefined; _wrapNativeSuper = function _wrapNativeSuper(Class) { if (Class === null || !_isNativeFunction(Class)) return Class; if (typeof Class !== "function") { throw new TypeError("Super expression must either be null or a function"); } if (typeof _cache !== "undefined") { if (_cache.has(Class)) return _cache.get(Class); _cache.set(Class, Wrapper); } function Wrapper() { return _construct(Class, arguments, _getPrototypeOf(this).constructor); } Wrapper.prototype = Object.create(Class.prototype, { constructor: { value: Wrapper, enumerable: false, writable: true, configurable: true } }); return _setPrototypeOf(Wrapper, Class); }; return _wrapNativeSuper(Class); } function _assertThisInitialized(self) { if (self === void 0) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return self; } function _possibleConstructorReturn(self, call) { if (call && (typeof call === "object" || typeof call === "function")) { return call; } return _assertThisInitialized(self); } var DEFAULT_VS = "\nattribute vec2 position;\n\nvoid main() {\n gl_Position = vec4(position, 0.0, 1.0);\n}"; var TEMPLATE = document.createElement('template'); TEMPLATE.innerHTML = "\n<style>\n :host {\n position: relative;\n display: inline-block;\n width: 250px;\n height: 250px;\n }\n :host > canvas {\n position: absolute;\n top: 0;\n left: 0;\n height: 100%;\n width: 100%;\n border-radius: inherit;\n }\n</style>\n"; var SHADERTOY_IO = /\(\s*out\s+vec4\s+(\S+)\s*,\s*in\s+vec2\s+(\S+)\s*\)/; var ShaderDoodle = /*#__PURE__*/ function (_HTMLElement) { _inherits(ShaderDoodle, _HTMLElement); function ShaderDoodle() { var _this; _classCallCheck(this, ShaderDoodle); _this = _possibleConstructorReturn(this, _getPrototypeOf(ShaderDoodle).call(this)); _this.shadow = _this.attachShadow({ mode: 'open' }); _this.shadow.appendChild(TEMPLATE.content.cloneNode(true)); return _this; } _createClass(ShaderDoodle, [{ key: "connectedCallback", value: function connectedCallback() { var _this2 = this; this.mounted = true; setTimeout(function () { if (!_this2.textContent.trim()) return false; try { _this2.init(); } catch (e) { _this2.textContent = ''; console.error(e && e.message || 'Error in shader-doodle.'); } }); } }, { key: "disconnectedCallback", value: function disconnectedCallback() { this.mounted = false; this.canvas.removeEventListener('mousedown', this.mouseDown); this.canvas.removeEventListener('mousemove', this.mouseMove); this.canvas.removeEventListener('mouseup', this.mouseUp); clearAnimationFrame(this.animationFrame); } }, { key: "init", value: function init() { var _this3 = this; this.useST = this.hasAttribute('shadertoy'); var fs = this.textContent; this.uniforms = { resolution: { name: this.useST ? 'iResolution' : 'u_resolution', type: 'vec2', value: [0, 0] }, time: { name: this.useST ? 'iTime' : 'u_time', type: 'float', value: 0 }, delta: { name: this.useST ? 'iTimeDelta' : 'u_delta', type: 'float', value: 0 }, date: { name: this.useST ? 'iDate' : 'u_date', type: 'vec4', value: [0, 0, 0, 0] }, frame: { name: this.useST ? 'iFrame' : 'u_frame', type: 'int', value: 0 }, mouse: { name: this.useST ? 'iMouse' : 'u_mouse', type: this.useST ? 'vec4' : 'vec2', value: this.useST ? [0, 0, 0, 0] : [0, 0] } }; this.canvas = document.createElement('canvas'); this.shadow.appendChild(this.canvas); var gl = this.gl = this.canvas.getContext('webgl'); this.updateRect(); // format/replace special shadertoy io if (this.useST) { var io = fs.match(SHADERTOY_IO); fs = fs.replace('mainImage', 'main'); fs = fs.replace(SHADERTOY_IO, '()'); fs = (io ? "#define ".concat(io[1], " gl_FragColor\n#define ").concat(io[2], " gl_FragCoord.xy\n") : '') + fs; } var uniformString = Object.values(this.uniforms).reduce(function (acc, uniform) { return acc + "uniform ".concat(uniform.type, " ").concat(uniform.name, ";\n"); }, ''); fs = uniformString + fs; fs = 'precision highp float;\n' + fs; gl.clearColor(0, 0, 0, 0); this.vertexShader = this.makeShader(gl.VERTEX_SHADER, DEFAULT_VS); this.fragmentShader = this.makeShader(gl.FRAGMENT_SHADER, fs); this.program = this.makeProgram(this.vertexShader, this.fragmentShader); this.vertices = new Float32Array([-1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1]); this.buffer = gl.createBuffer(); gl.bindBuffer(gl.ARRAY_BUFFER, this.buffer); gl.bufferData(gl.ARRAY_BUFFER, this.vertices, gl.STATIC_DRAW); gl.useProgram(this.program); this.program.position = gl.getAttribLocation(this.program, 'position'); gl.enableVertexAttribArray(this.program.position); gl.vertexAttribPointer(this.program.position, 2, gl.FLOAT, false, 0, 0); // get all uniform locations from shaders Object.values(this.uniforms).forEach(function (uniform) { uniform.location = gl.getUniformLocation(_this3.program, uniform.name); }); this._bind('mouseDown', 'mouseMove', 'mouseUp', 'render'); this.canvas.addEventListener('mousedown', this.mouseDown); this.canvas.addEventListener('mousemove', this.mouseMove); this.canvas.addEventListener('mouseup', this.mouseUp); this.render(); } }, { key: "render", value: function render(timestamp) { if (!this || !this.mounted || !this.gl) return; var gl = this.gl; this.updateTimeUniforms(timestamp); this.updateRect(); gl.clear(gl.COLOR_BUFFER_BIT); Object.values(this.uniforms).forEach(function (_ref) { var type = _ref.type, location = _ref.location, value = _ref.value; var method = type.match(/vec/) ? "".concat(type[type.length - 1], "fv") : "1".concat(type[0]); gl["uniform".concat(method)](location, value); }); gl.drawArrays(gl.TRIANGLES, 0, this.vertices.length / 2); this.ticking = false; this.animationFrame = requestAnimationFrame(this.render); } }, { key: "mouseDown", value: function mouseDown(e) { if (this.useST) { this.mousedown = true; var _this$rect = this.rect, top = _this$rect.top, left = _this$rect.left, height = _this$rect.height; this.uniforms.mouse.value[2] = e.clientX - Math.floor(left); this.uniforms.mouse.value[3] = Math.floor(height) - (e.clientY - Math.floor(top)); } } }, { key: "mouseMove", value: function mouseMove(e) { if (!this.ticking && (!this.useST || this.mousedown)) { var _this$rect2 = this.rect, top = _this$rect2.top, left = _this$rect2.left, height = _this$rect2.height; this.uniforms.mouse.value[0] = e.clientX - Math.floor(left); this.uniforms.mouse.value[1] = Math.floor(height) - (e.clientY - Math.floor(top)); this.ticking = true; } } }, { key: "mouseUp", value: function mouseUp(e) { if (this.useST) { this.mousedown = false; this.uniforms.mouse.value[2] = 0; this.uniforms.mouse.value[3] = 0; } } }, { key: "updateTimeUniforms", value: function updateTimeUniforms(timestamp) { var delta = this.lastTime ? (timestamp - this.lastTime) / 1000 : 0; this.lastTime = timestamp; this.uniforms.time.value += delta; this.uniforms.delta.value = delta; this.uniforms.frame.value++; var d = new Date(); this.uniforms.date.value[0] = d.getFullYear(); this.uniforms.date.value[1] = d.getMonth() + 1; this.uniforms.date.value[2] = d.getDate(); this.uniforms.date.value[3] = d.getHours() * 60 * 60 + d.getMinutes() * 60 + d.getSeconds() + d.getMilliseconds() * 0.001; } }, { key: "updateRect", value: function updateRect() { this.rect = this.canvas.getBoundingClientRect(); var _this$rect3 = this.rect, width = _this$rect3.width, height = _this$rect3.height; var widthChanged = this.canvas.width !== width; var heightChanged = this.canvas.height !== height; if (widthChanged) { this.canvas.width = this.uniforms.resolution.value[0] = width; } if (heightChanged) { this.canvas.height = this.uniforms.resolution.value[1] = height; } if (widthChanged || heightChanged) { this.gl.viewport(0, 0, this.canvas.width, this.canvas.height); } } }, { key: "makeShader", value: function makeShader(type, string) { var gl = this.gl; var shader = gl.createShader(type); gl.shaderSource(shader, string); gl.compileShader(shader); if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) { var compilationLog = gl.getShaderInfoLog(shader); gl.deleteShader(shader); console.warn(compilationLog, '\nin shader:\n', string); } return shader; } }, { key: "makeProgram", value: function makeProgram() { var gl = this.gl; var program = gl.createProgram(); for (var _len = arguments.length, shaders = new Array(_len), _key = 0; _key < _len; _key++) { shaders[_key] = arguments[_key]; } shaders.forEach(function (shader) { gl.attachShader(program, shader); }); gl.linkProgram(program); if (!gl.getProgramParameter(program, gl.LINK_STATUS)) { var linkLog = gl.getProgramInfoLog(this.program); console.warn(linkLog); } return program; } }, { key: "_bind", value: function _bind() { var _this4 = this; for (var _len2 = arguments.length, methods = new Array(_len2), _key2 = 0; _key2 < _len2; _key2++) { methods[_key2] = arguments[_key2]; } methods.forEach(function (method) { return _this4[method] = _this4[method].bind(_this4); }); } }]); return ShaderDoodle; }(_wrapNativeSuper(HTMLElement)); customElements.define('shader-doodle', ShaderDoodle);