PIXI.ParticleContainer로 많은 양의 스프라이트를 고성능으로 처리해보자

PIXI.ParticleContainer에 담으면 많은 수의 스프라이트를 좀더 가볍게 처리할 수 있다

단점은 제한된 기능만 사용할 수 있다

그렇다해도 화면표현에 대해서는 꽤 많은것을 할 수 있으니 큰 제약이 되지 않는다

 

bGV0IGdldF9hbmdsZV9pbl9yYWRpYW5fYmV0d2Vlbl90d29fcG9pbnRzID0gZnVuY3Rpb24gKHBvaW50MSwgcG9pbnQyKSB7CiAgIHJldHVybiBNYXRoLmF0YW4yKHBvaW50Mi55IC0gcG9pbnQxLnksIHBvaW50Mi54IC0gcG9pbnQxLngpOwp9OwpsZXQgZ2V0X2Rpc3RhbmNlX2JldHdlZW5fdHdvX3BvaW50ID0gZnVuY3Rpb24gKHBvaW50MSwgcG9pbnQyKSB7CiAgIHZhciBhID0gcG9pbnQxLnggLSBwb2ludDIueDsKICAgdmFyIGIgPSBwb2ludDEueSAtIHBvaW50Mi55OwogICByZXR1cm4gTWF0aC5zcXJ0KGEgKiBhICsgYiAqIGIpOwp9CmZ1bmN0aW9uIGdldF9wb2ludF9ieV9yYWRpYW4oeCwgeSwgbGVuZ3RoLCBhbmdsZSkgewogICByZXR1cm4gWwogICAgICB4ICsgTWF0aC5jb3MoYW5nbGUpICogbGVuZ3RoLAogICAgICB5ICsgTWF0aC5zaW4oYW5nbGUpICogbGVuZ3RoCiAgIF07Cn0KCndpbmRvdy5hZGRFdmVudExpc3RlbmVyKCdsb2FkJywgZnVuY3Rpb24gKCkgewogICAoZnVuY3Rpb24gKCkgeyB2YXIgc2NyaXB0ID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgnc2NyaXB0Jyk7IHNjcmlwdC5vbmxvYWQgPSBmdW5jdGlvbiAoKSB7IHZhciBzdGF0cyA9IG5ldyBTdGF0cygpOyBkb2N1bWVudC5ib2R5LmFwcGVuZENoaWxkKHN0YXRzLmRvbSk7IHJlcXVlc3RBbmltYXRpb25GcmFtZShmdW5jdGlvbiBsb29wKCkgeyBzdGF0cy51cGRhdGUoKTsgcmVxdWVzdEFuaW1hdGlvbkZyYW1lKGxvb3ApIH0pOyB9OyBzY3JpcHQuc3JjID0gJy8vbXJkb29iLmdpdGh1Yi5pby9zdGF0cy5qcy9idWlsZC9zdGF0cy5taW4uanMnOyBkb2N1bWVudC5oZWFkLmFwcGVuZENoaWxkKHNjcmlwdCk7IH0pKCk7CgoKICAgZnVuY3Rpb24gbWFrZVBpeGkobW9kZSkgewogICAgICBsZXQgcGFyZW50ID0gZG9jdW1lbnQucXVlcnlTZWxlY3RvcignI2NvbnRhaW5lcl93aW50ZXInKTsKICAgICAgLy8gcGFyZW50LmlubmVySFRNTCA9ICcnOwogICAgICBsZXQgcGFyZW50U2l6ZSA9IHBhcmVudC5nZXRCb3VuZGluZ0NsaWVudFJlY3QoKTsKICAgICAgbGV0IHdpZHRoID0gcGFyZW50U2l6ZS53aWR0aCAvIHdpbmRvdy5kZXZpY2VQaXhlbFJhdGlvOwogICAgICBsZXQgaGVpZ2h0ID0gcGFyZW50U2l6ZS53aWR0aCAvIHdpbmRvdy5kZXZpY2VQaXhlbFJhdGlvOwogICAgICBsZXQgYXBwID0gbmV3IFBJWEkuQXBwbGljYXRpb24oewogICAgICAgICB3aWR0aCwKICAgICAgICAgaGVpZ2h0LAogICAgICAgICBhbnRpYWxpYXM6IHRydWUsCiAgICAgICAgIHRyYW5zcGFyZW50OiB0cnVlLAogICAgICAgICByZXNvbHV0aW9uOiB3aW5kb3cuZGV2aWNlUGl4ZWxSYXRpbywKICAgICAgfSk7CgogICAgICBhcHAudmlldy5zZXRBdHRyaWJ1dGUoJ2lkJywgJ3NjcmVlbicpOwogICAgICBwYXJlbnQuYXBwZW5kQ2hpbGQoYXBwLnZpZXcpOwoKICAgICAgY29uc3Qgc2VnID0gMTAwMDA7CiAgICAgIGNvbnN0IGNvbnRhaW5lciA9IG5ldyBQSVhJLlBhcnRpY2xlQ29udGFpbmVyKHNlZywgeyBhbHBoYTogdHJ1ZSwgcm90YXRpb246IHRydWUgfSk7CiAgICAgIGFwcC5zdGFnZS5hZGRDaGlsZChjb250YWluZXIpOwoKICAgICAgY2xhc3MgUmVjdGFuZ2xlIGV4dGVuZHMgUElYSS5TcHJpdGUgewogICAgICAgICBjb25zdHJ1Y3RvcigpIHsKICAgICAgICAgICAgLy9QSVhJLlRleHR1cmUuV0hJVEUKICAgICAgICAgICAgY29uc3QgdGV4dHVyZSA9IFBJWEkuVGV4dHVyZS5mcm9tKCdkYXRhOmltYWdlL3BuZztiYXNlNjQsaVZCT1J3MEtHZ29BQUFBTlNVaEVVZ0FBQUFFQUFBQUJDQVFBQUFDMUhBd0NBQUFBQzBsRVFWUjQybVA4L3g4QUF3TUNBTytpcDFzQUFBQUFTVVZPUks1Q1lJST0nKTsKICAgICAgICAgICAgc3VwZXIodGV4dHVyZSk7CiAgICAgICAgIH0KICAgICAgfQogICAgICBjbGFzcyBMaW5lIGV4dGVuZHMgUmVjdGFuZ2xlIHsKICAgICAgICAgc3BlYyA9IHt9CiAgICAgICAgIGNvbnN0cnVjdG9yKCkgewogICAgICAgICAgICBzdXBlcigpOwogICAgICAgICAgICB0aGlzLmFuY2hvci55ID0gMC41OwogICAgICAgICAgICB0aGlzLmF4aXMgPSAwLjU7CiAgICAgICAgICAgIHRoaXMudGhpY2tuZXNzID0gMTsKICAgICAgICAgfQogICAgICAgICBzZXQgdGhpY2tuZXNzKHYpIHsgdGhpcy5oZWlnaHQgPSB2OyB9CiAgICAgICAgIGdldCB0aGlja25lc3MoKSB7IHJldHVybiB0aGlzLmhlaWdodDsgfQogICAgICAgICBzZXQgYXhpcyh2KSB7IHRoaXMuYW5jaG9yLnggPSB2OyB9CiAgICAgICAgIGdldCBheGlzKCkgeyByZXR1cm4gdGhpcy5hbmNob3IueDsgfQogICAgICAgICBzZXQgYXgodikgeyB0aGlzLnNwZWMuYXggPSB2OyB0aGlzLmNhbGNwb3MoKTsgfQogICAgICAgICBnZXQgYXgoKSB7IHJldHVybiB0aGlzLnNwZWMuYXggPz8gMDsgfQogICAgICAgICBzZXQgYXkodikgeyB0aGlzLnNwZWMuYXkgPSB2OyB0aGlzLmNhbGNwb3MoKTsgfQogICAgICAgICBnZXQgYXkoKSB7IHJldHVybiB0aGlzLnNwZWMuYXkgPz8gMDsgfQogICAgICAgICBzZXQgYngodikgeyB0aGlzLnNwZWMuYnggPSB2OyB0aGlzLmNhbGNwb3MoKTsgfQogICAgICAgICBnZXQgYngoKSB7IHJldHVybiB0aGlzLnNwZWMuYnggPz8gMDsgfQogICAgICAgICBzZXQgYnkodikgeyB0aGlzLnNwZWMuYnkgPSB2OyB0aGlzLmNhbGNwb3MoKTsgfQogICAgICAgICBnZXQgYnkoKSB7IHJldHVybiB0aGlzLnNwZWMuYnkgPz8gMDsgfQogICAgICAgICBnZXQgbGVuZ3RoKCkgewogICAgICAgICAgICBsZXQgYSA9IHRoaXMuYXggLSB0aGlzLmJ4OwogICAgICAgICAgICBsZXQgYiA9IHRoaXMuYXkgLSB0aGlzLmJ5OwogICAgICAgICAgICByZXR1cm4gTWF0aC5zcXJ0KGEgKiBhICsgYiAqIGIpOwogICAgICAgICB9CiAgICAgICAgIGNhbGNwb3MoKSB7CiAgICAgICAgICAgIHRoaXMud2lkdGggPSB0aGlzLmxlbmd0aDsKICAgICAgICAgICAgdGhpcy54ID0gdGhpcy5ieCAtICgodGhpcy5ieCAtIHRoaXMuYXgpICogKDEgLSB0aGlzLmFuY2hvci54KSk7CiAgICAgICAgICAgIHRoaXMueSA9IHRoaXMuYnkgLSAoKHRoaXMuYnkgLSB0aGlzLmF5KSAqICgxIC0gdGhpcy5hbmNob3IueCkpOwogICAgICAgICAgICB0aGlzLnJvdGF0aW9uID0gTWF0aC5hdGFuMih0aGlzLmJ5IC0gdGhpcy5heSwgdGhpcy5ieCAtIHRoaXMuYXgpOwogICAgICAgICB9CiAgICAgIH0KICAgICAgY29uc3QgbGlzdCA9IFtdOwogICAgICBjb25zdCBsZW4gPSB3aWR0aCAqIDAuNTsKICAgICAgZm9yIChsZXQgaSA9IDA7IGkgPCBzZWc7IGkrKykgewogICAgICAgICBsZXQgbG4gPSBuZXcgTGluZSgpOwogICAgICAgICBsbi50aW50ID0gMHgwMDAwMDA7CiAgICAgICAgIGxuLnRoaWNrbmVzcyA9IDAuMDM7CiAgICAgICAgIGxuLmF4ID0gMC41ICogd2lkdGg7CiAgICAgICAgIGxuLmF5ID0gMC41ICogd2lkdGg7CiAgICAgICAgIGxldCBicmFkaWFuID0gKE1hdGguUEkgKiAyIC8gc2VnKSAqIGk7CiAgICAgICAgIGxldCByb3RjbnQgPSAwOwogICAgICAgICBsbi5icG9pbnQgPSAoKSA9PiB7CiAgICAgICAgICAgIGxldCBbeCwgeV0gPSBnZXRfcG9pbnRfYnlfcmFkaWFuKGxuLmF4LCBsbi5heSwgbGVuLCBicmFkaWFuICsgcm90Y250KTsKICAgICAgICAgICAgbG4uYnggPSB4OwogICAgICAgICAgICBsbi5ieSA9IHk7CiAgICAgICAgIH0KICAgICAgICAgbG4uYW5pID0gKCkgPT4gewogICAgICAgICAgICByb3RjbnQgKz0gMC4wMDAwNTsKICAgICAgICAgICAgbG4uYnBvaW50KCk7CiAgICAgICAgIH0KICAgICAgICAgbG4uYnBvaW50KCk7CiAgICAgICAgIGxpc3QucHVzaChsbik7CiAgICAgICAgIChtb2RlID8gY29udGFpbmVyIDogYXBwLnN0YWdlKS5hZGRDaGlsZChsbik7CiAgICAgIH0KICAgICAgbGV0IHVwZGF0ZSA9IGZ1bmN0aW9uICgpIHsKICAgICAgICAgLy8gaWYgKCFhcHAudmlldy5wYXJlbnROb2RlKSByZXR1cm4gZmFsc2U7CiAgICAgICAgIGZvciAobGV0IGkgPSAwOyBpIDwgbGlzdC5sZW5ndGg7IGkrKykgewogICAgICAgICAgICBsZXQgbG4gPSBsaXN0W2ldOwogICAgICAgICAgICBsbi5hbmkoKTsKICAgICAgICAgfQogICAgICB9OwogICAgICBQSVhJLlRpY2tlci5zaGFyZWQuYWRkKHVwZGF0ZSk7CgogICAgICByZXR1cm4gewogICAgICAgICBkcm9wKCkgewogICAgICAgICAgICBQSVhJLlRpY2tlci5zaGFyZWQucmVtb3ZlKHVwZGF0ZSk7CiAgICAgICAgICAgIC8vIGFwcC5zdGFnZS5kZXN0cm95KHRydWUpOwogICAgICAgICAgICBjb250YWluZXIuY2hpbGRyZW4uZm9yRWFjaChmdW5jdGlvbiAoYykgeyBjb250YWluZXIucmVtb3ZlQ2hpbGQoYykgfSkKICAgICAgICAgICAgYXBwLnN0YWdlLmNoaWxkcmVuLmZvckVhY2goZnVuY3Rpb24gKGMpIHsgYXBwLnN0YWdlLnJlbW92ZUNoaWxkKGMpIH0pCgogICAgICAgICAgICBhcHAuZGVzdHJveSh0cnVlKTsKICAgICAgICAgICAgLy8gYXBwLnN0YWdlID0gbnVsbDsKCiAgICAgICAgICAgIGFwcCA9IG51bGw7CiAgICAgICAgICAgIHdoaWxlIChsaXN0Lmxlbmd0aCkgbGlzdC5zcGxpY2UoMCwgbGlzdC5sZW5ndGgpOwogICAgICAgICB9CiAgICAgIH0KICAgfQogICBsZXQgcGl4aTsKICAgZG9jdW1lbnQucXVlcnlTZWxlY3RvcignI2J0bmZhc3QnKS5hZGRFdmVudExpc3RlbmVyKCdjbGljaycsIGUgPT4gewogICAgICBwaXhpICYmIHBpeGkuZHJvcCgpOwogICAgICBwaXhpID0gbWFrZVBpeGkoIWZhbHNlKTsKICAgfSkKICAgZG9jdW1lbnQucXVlcnlTZWxlY3RvcignI2J0bnNsb3cnKS5hZGRFdmVudExpc3RlbmVyKCdjbGljaycsIGUgPT4gewogICAgICBwaXhpICYmIHBpeGkuZHJvcCgpOwogICAgICBwaXhpID0gbWFrZVBpeGkoIXRydWUpOwogICB9KQogICBidG5mYXN0LmNsaWNrKCk7Cgp9KQ==
let get_angle_in_radian_between_two_points = function (point1, point2) { return Math.atan2(point2.y - point1.y, point2.x - point1.x); }; let get_distance_between_two_point = function (point1, point2) { var a = point1.x - point2.x; var b = point1.y - point2.y; return Math.sqrt(a * a + b * b); } function get_point_by_radian(x, y, length, angle) { return [ x + Math.cos(angle) * length, y + Math.sin(angle) * length ]; } window.addEventListener('load', function () { (function () { var script = document.createElement('script'); script.onload = function () { var stats = new Stats(); document.body.appendChild(stats.dom); requestAnimationFrame(function loop() { stats.update(); requestAnimationFrame(loop) }); }; script.src = '//mrdoob.github.io/stats.js/build/stats.min.js'; document.head.appendChild(script); })(); function makePixi(mode) { let parent = document.querySelector('#container_winter'); // parent.innerHTML = ''; let parentSize = parent.getBoundingClientRect(); let width = parentSize.width / window.devicePixelRatio; let height = parentSize.width / window.devicePixelRatio; let app = new PIXI.Application({ width, height, antialias: true, transparent: true, resolution: window.devicePixelRatio, }); app.view.setAttribute('id', 'screen'); parent.appendChild(app.view); const seg = 10000; const container = new PIXI.ParticleContainer(seg, { alpha: true, rotation: true }); app.stage.addChild(container); class Rectangle extends PIXI.Sprite { constructor() { //PIXI.Texture.WHITE const texture = PIXI.Texture.from('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mP8/x8AAwMCAO+ip1sAAAAASUVORK5CYII='); super(texture); } } class Line extends Rectangle { spec = {} constructor() { super(); this.anchor.y = 0.5; this.axis = 0.5; this.thickness = 1; } set thickness(v) { this.height = v; } get thickness() { return this.height; } set axis(v) { this.anchor.x = v; } get axis() { return this.anchor.x; } set ax(v) { this.spec.ax = v; this.calcpos(); } get ax() { return this.spec.ax ?? 0; } set ay(v) { this.spec.ay = v; this.calcpos(); } get ay() { return this.spec.ay ?? 0; } set bx(v) { this.spec.bx = v; this.calcpos(); } get bx() { return this.spec.bx ?? 0; } set by(v) { this.spec.by = v; this.calcpos(); } get by() { return this.spec.by ?? 0; } get length() { let a = this.ax - this.bx; let b = this.ay - this.by; return Math.sqrt(a * a + b * b); } calcpos() { this.width = this.length; this.x = this.bx - ((this.bx - this.ax) * (1 - this.anchor.x)); this.y = this.by - ((this.by - this.ay) * (1 - this.anchor.x)); this.rotation = Math.atan2(this.by - this.ay, this.bx - this.ax); } } const list = []; const len = width * 0.5; for (let i = 0; i < seg; i++) { let ln = new Line(); ln.tint = 0x000000; ln.thickness = 0.03; ln.ax = 0.5 * width; ln.ay = 0.5 * width; let bradian = (Math.PI * 2 / seg) * i; let rotcnt = 0; ln.bpoint = () => { let [x, y] = get_point_by_radian(ln.ax, ln.ay, len, bradian + rotcnt); ln.bx = x; ln.by = y; } ln.ani = () => { rotcnt += 0.00005; ln.bpoint(); } ln.bpoint(); list.push(ln); (mode ? container : app.stage).addChild(ln); } let update = function () { // if (!app.view.parentNode) return false; for (let i = 0; i < list.length; i++) { let ln = list[i]; ln.ani(); } }; PIXI.Ticker.shared.add(update); return { drop() { PIXI.Ticker.shared.remove(update); // app.stage.destroy(true); container.children.forEach(function (c) { container.removeChild(c) }) app.stage.children.forEach(function (c) { app.stage.removeChild(c) }) app.destroy(true); // app.stage = null; app = null; while (list.length) list.splice(0, list.length); } } } let pixi; document.querySelector('#btnfast').addEventListener('click', e => { pixi && pixi.drop(); pixi = makePixi(!false); }) document.querySelector('#btnslow').addEventListener('click', e => { pixi && pixi.drop(); pixi = makePixi(!true); }) btnfast.click(); })