Pen Settings

HTML

CSS

CSS Base

Vendor Prefixing

Add External Stylesheets/Pens

Any URL's added here will be added as <link>s in order, and before the CSS in the editor. If you link to another Pen, it will include the CSS from that Pen. If the preprocessor matches, it will attempt to combine them before processing.

+ add another resource

JavaScript

Babel includes JSX processing.

Add External Scripts/Pens

Any URL's added here will be added as <script>s in order, and run before the JavaScript in the editor. You can use the URL of any other Pen and it will include the JavaScript from that Pen.

+ add another resource

Packages

Add Packages

Search for and use JavaScript packages from npm here. By selecting a package, an import statement will be added to the top of the JavaScript editor for this package.

Behavior

Save Automatically?

If active, Pens will autosave every 30 seconds after being saved once.

Auto-Updating Preview

If enabled, the preview panel updates automatically as you code. If disabled, use the "Run" button to update.

Format on Save

If enabled, your code will be formatted when you actively save your Pen. Note: your code becomes un-folded during formatting.

Editor Settings

Code Indentation

Want to change your Syntax Highlighting theme, Fonts and more?

Visit your global Editor Settings.

HTML

              
                <script>
  console.clear();
  var log = console.log.bind(console)
</script>

<canvas id="canvas"></canvas>

<!-- <div class="controls">
  <button id="run-button">Run</button>
</div>

<script>
  function runner(fn, val, samples){
    for(var i = 0; i < samples; i++){
       fn(val);
    }
  }
</script> -->
              
            
!

CSS

              
                #canvas {
  position: fixed;
  width: 100%;
  height: 100%;
  touch-action: none;
  z-index: 2;
}

.controls {
  position: fixed;
  z-index: 2;
  top: 0;
  left: 0;
  padding: 10px;
  background: rgba(255,255,255,0.7);
}
              
            
!

JS

              
                // console.clear();
// const log = console.log.bind(console);

namespace PixiSvg {
  
  // import BaseTextureCache = PIXI.utils.BaseTextureCache;
  // import TextureCache = PIXI.utils.TextureCache;
  // import EventEmitter = PIXI.utils.EventEmitter
  
  const Texture = PIXI.Texture;
  const Resource = PIXI.loaders.Resource;
  const Rectangle = PIXI.Rectangle;
  const settings = PIXI.settings;
  const {
    uid, getUrlFileExtension, decomposeDataUri, getSvgSize, EventEmitter,
    getResolutionOfUrl, BaseTextureCache, TextureCache,
  } = PIXI.utils;
  
  
  function nextPow2(v) {
    v += v === 0;
    --v;
    v |= v >>> 1;
    v |= v >>> 2;
    v |= v >>> 4;
    v |= v >>> 8;
    v |= v >>> 16;
    return v + 1;
  }
  
  //
  // SVG FRAME
  // ===========================================================================
  // class Rectangle extends PIXI.Rectangle {
    // constructor(x = 0, y = 0, width = 0, height = 0, sourceScale = 1) {
  export class SvgFrame {
    
    updateId = 0;
    
    constructor(frame, sourceScale = 1) {

      this._x = Number(frame.x);
      this._y = Number(frame.y);
      this._width = Math.max(Number(frame.width), 1);
      this._height = Math.max(Number(frame.height), 1);
      this.sourceScale = sourceScale;
    }

    get sourceScale() {
      return this._sourceScale;
    }

    set sourceScale(scale) {
      
      this.updateId++;
      
      this._sourceScale = scale;
      // this.x = Math.floor(this._x * scale);
      // this.y = Math.floor(this._y * scale);
      // this.width = Math.max(Math.floor(this._width * scale), 1);
      // this.height = Math.max(Math.floor(this._height * scale), 1);
      
      // this.x = Math.round(this._x * scale);
      // this.y = Math.round(this._y * scale);
      // this.width = Math.max(Math.round(this._width * scale), 1);
      // this.height = Math.max(Math.round(this._height * scale), 1);
      
      this.x = this._x * scale;
      this.y = this._y * scale;
      this.width = Math.max(this._width * scale, 1);
      this.height = Math.max(this._height * scale, 1);
    }
  }

  //
  // SVG BASE TEXTURE
  // ===========================================================================
  export class SvgBaseTexture {
    
    // width = 256;
    // height = 256;
    // realWidth = 256;
    // realHeight = 256;
    _sourceScale = 1.0;

    constructor(svgTexture, sourceScale = 1) {        
      
      this.svgSource = this.origSource = svgTexture.origSource || svgTexture.source;
      this.svgWidth = this.width = svgTexture.width;
      this.svgHeight = this.height = svgTexture.height;
      this.svgSize = new Rectangle(0, 0, svgTexture.width, svgTexture.height);
      
      
      const size = nextPow2(Math.max(this.width, this.height));
      
      // log("POW2 SIZE", size)
      
      this.canvas = document.createElement("canvas");
      this.context = this.canvas.getContext("2d");       
      // this.canvas.width = this.realWidth = size; 
      // this.canvas.height = this.realHeight = size;
      
      this.canvas.width = this.realWidth = this.svgWidth; 
      this.canvas.height = this.realHeight = this.svgHeight;
            
      this.baseTexture = PIXI.BaseTexture.fromCanvas(this.canvas);
      
      this.sourceScale = sourceScale;
    }
    
    get sourceScale() {
      return this._sourceScale;
    }
    
    set sourceScale(value) {
      this._sourceScale = value;      
      this.baseTexture.sourceScale = value;      
      this.update();
    }
    
    update() {
      
      const scale = this.sourceScale;
      const dw = this.svgWidth;
      const dh = this.svgHeight;
      const sw = Math.round(dw * scale);
      const sh = Math.round(dh * scale);  
      // const sw = Math.floor(dw * scale);
      // const sh = Math.floor(dh * scale);  
      const size = nextPow2(Math.max(sw, sh));
      
      log("POW2 SIZE", size)
      
      this.canvas.width = this.canvas.height = size;
      
      // this.canvas.width = sw
      // this.canvas.height = sh
      
      this.context.drawImage(this.svgSource, 0, 0, dw, dh, 0, 0, sw, sh);      
      this.baseTexture.update();      
      // return this;
    }
  }
  
  //
  // SVG SPRITE SHEET
  // =========================================================================== 
  export class SvgSpriteSheet {
      
    _batchIndex = 0;
    _callback = null;
    textures = {};
    svgFrames = {};
    
    constructor(public svgBaseTexture, public data, resolutionFilename = null) {
      
      this.baseTexture = svgBaseTexture.baseTexture;     
      this._frames = data.svgFrames;
      this._frameKeys = Object.keys(this._frames);
    }
    
    static get BATCH_SIZE() {
      return 1000;
    }

    parse(callback) {

      this._batchIndex = 0;
      this._callback = callback;
      
      // log("PARSE STSART", this._frameKeys.length)
      
      if (this._frameKeys.length <= SvgSpriteSheet.BATCH_SIZE) {
        
        this._processFrames(0);
        this._parseComplete();
        
      } else {
        this._nextBatch();
      }     
    }

    _processFrames(initialFrameIndex) {
      
      let frameIndex = initialFrameIndex;
      const maxFrames = SvgSpriteSheet.BATCH_SIZE;
      const sourceScale = this.baseTexture.sourceScale;

      while (frameIndex - initialFrameIndex < maxFrames && frameIndex < this._frameKeys.length) {
        
        const i = this._frameKeys[frameIndex];
        const sourceSize = this._frames[i].sourceSize;
        const layoutSize = this._frames[i].layoutSize;
        
        // log("PARSE", i)
        
        if (sourceSize && layoutSize) {
                   
          const origSize = new SvgFrame({
            x: 0,
            y: 0,
            width: sourceSize.width,
            height: sourceSize.height,
          }, 1);
          
          const orig = new SvgFrame(origSize, sourceScale);
          const sourceFrame = new SvgFrame(sourceSize, sourceScale);
          const layoutFrame = new SvgFrame(layoutSize, sourceScale);
          
          const texture = this.textures[i] = new Texture(
            this.baseTexture,
            sourceFrame,
            null,
            null,
            0
          );
          
          texture.svgFrames = this.svgFrames[i] = {
            orig, 
            sourceFrame,
            layoutFrame
          };
          
          Texture.addToCache(texture, i);
        }
        
        frameIndex++;
      }
      
      
      // log("PROCESS FRAMES", this)
    }

    _parseComplete() {

      const callback = this._callback;
      this._callback = null;
      this._batchIndex = 0;
      callback.call(this, this.textures);
      // callback.call(this, this.textures);
    }
    
    _nextBatch() {
      
      this._processFrames(this._batchIndex * SvgSpriteSheet.BATCH_SIZE);
      this._batchIndex++;
      
      setTimeout(() => {
        
        if (this._batchIndex * SvgSpriteSheet.BATCH_SIZE < this._frameKeys.length) {
          this._nextBatch();
        } else {
          this._parseComplete();
        }        
      }, 0);
    }
    
    destroy(destroyBase = false) {
      
      for (const i in this.textures) {
        this.textures[i].destroy();
      }
      
      this._frames = null;
      this._frameKeys = null;
      this.data = null;
      this.textures = null;
      
      if (destroyBase) {
        this.baseTexture.destroy();
        this.svgBaseTexture.destroy();
      }
      
      this.baseTexture = null;
      this.svgBaseTexture = null;
    }
  }
  
  //
  // SVG SPRITE SHEET PARSER
  // =========================================================================== 
  export function svgSpriteSheetParser(resource, next) {

    const svgResourceName = `${resource.name}_svg`;

    if (!resource.data 
       || resource.type !== Resource.TYPE.JSON
       || !resource.data.svgFrames
       || this.resources[svgResourceName]) {

      next();
      return;
    }

    const loadOptions = {
      crossOrigin: resource.crossOrigin,
      loadType: Resource.LOAD_TYPE.IMAGE,
      metadata: resource.metadata.imageMetadata,
      parentResource: resource,
    };
    
    // log("LOAD OPTIONS", loadOptions)

    const resourcePath = PIXI.loaders.getResourcePath(resource, this.baseUrl);

    this.add(svgResourceName, resourcePath, loadOptions, function onImageLoad(res) {
      
      const svgBaseTexture = new SvgBaseTexture(res.texture.baseTexture, 1);
      
      const spriteSheet = new SvgSpriteSheet(
        svgBaseTexture,
        resource.data,
        resource.url
      );
      
      // const spriteSheet = new SvgSpriteSheet(
      //   res.texture.baseTexture,
      //   resource.data,
      //   resource.url
      // );

      spriteSheet.parse(() => {
        resource.spritesheet = spriteSheet;
        resource.textures = spriteSheet.textures;
        next();
      });
    });  
  }
  
  // PIXI.loaders.Loader.addPixiMiddleware(atlasChecker);
	// PIXI.loader.use(atlasChecker());
  PIXI.loaders.Loader.addPixiMiddleware(() => svgSpriteSheetParser);
	PIXI.loader.use(svgSpriteSheetParser);
  
  PIXI.svg = PixiSvg;
}

//
// SVG SPRITE
// =========================================================================== 
class SvgSprite extends PIXI.Sprite {
  
  constructor(texture) {
    super(texture);
    
    this.svgFrames = texture.svgFrames;
    
    this.update();
    
    this.timeline = new TimelineMax({
      autoRemoveChildren: true,
      onComplete: this.tweenTint,
      callbackScope: this,
    })
    
    // this.timeline.progress(Math.random())
    
    TweenLite.set(this, {
      pixi: {
        tint: `hsl(${random(360)}, ${random(90, 100)}%, ${random(50, 60)}%)`
      }
    })
    
    setTimeout(() => {
      this.timeline.progress(Math.random())
    }, 1)
  }
  
  tweenTint() {
    
    this.timeline.to(this, random(5, 10), {
      pixi: {
        tint: `hsl(${random(360)}, ${random(90, 100)}%, ${random(50, 60)}%)`
        // tint: `hsl(+=${random(360)}, ${random(90, 100)}%, ${random(50, 60)}%)`
      }
    })
  }
  
  update() {
    
    const layout = this.svgFrames.layoutFrame;
    this.x = layout.x;
    this.y = layout.y;
    
    this.texture._updateUvs();
  }
}

//
// APP
// =========================================================================== 
PIXI.settings.MIPMAP_TEXTURES = false;


// https://s3-us-west-2.amazonaws.com/s.cdpn.io/106114/icons.json

const baseUrl = "https://s3-us-west-2.amazonaws.com/s.cdpn.io/106114/";

// const Resource = PIXI.loaders.Resource;
// const BaseTextureCache = PIXI.utils.BaseTextureCache;
// const TextureCache = PIXI.utils.TextureCache;


var view = document.querySelector("#canvas");
var vw = window.innerWidth;
var vh = window.innerHeight;

var app = new PIXI.Application({
  view: view,
  width: vw,
  height: vh,
  // backgroundColor: 0x1a237e,
  // antialias: true,
});

log("PIXI", PIXI)

var loader = new PIXI.loaders.Loader(baseUrl)
  .add("icons", "icons8.json?v=1")
  .load(onAssetsLoaded)

function onAssetsLoaded(loader, resources) {
  // var body = new PIXI.Sprite.fromFrame('body.png');
  
  log("RESOURCES", resources)
  
  const _container = new PIXI.Container();
  const container = new PIXI.particles.ParticleContainer(1500, {
    scale: true,
    position: true,
    rotation: false,
    uvs: true,
    tint: true
  })
  
  const spriteSheet = resources.icons.spritesheet;
  const svgFrames = spriteSheet.svgFrames;
  const svgTexture = spriteSheet.svgBaseTexture;
  const textures = spriteSheet.textures;
  const textureCache = PIXI.utils.textureCache;
  
  const frameKeys = spriteSheet._frameKeys.filter(key => {
    return key !== "svg" && key !== "container"
  })
  
  // log("\n")
  // log("SVG TEXTURE", svgTexture)
  // log("SVG FRAMES", svgFrames)
  // log("TEXTURES", textures)
  // log("TEXTURE CACHE", textureCache)
  // log("FRAME KEYS", frameKeys)
  // log("\n")
  
  const sprites = spriteSheet._frameKeys.reduce((res, key) => {
    
    if (key !== "svg" && key !== "container") {
      
      const texture = PIXI.Texture.fromFrame(key);
      const sprite = new SvgSprite(texture);
      container.addChild(sprite);
      res.push(sprite);
      
      // return sprite;
      
    }
    return res;
  }, []);
  
  // log("SPRITES", sprites)
  
  let resized = true;
  
  const filter = new PIXI.filters.AlphaFilter()
  app.stage.filterArea = app.screen;
  app.stage.filters = [filter];
  app.stage.addChild(container)
  app.ticker.add(onFrame);
  
  // TweenMax.to(filter, 2, {
  //   alpha: 0,
  //   yoyo: true,
  //   repeat: -1,
  //   repeatDelay: 1,
  // })
  
  window.addEventListener("resize", () => resized = true);
    
  function onFrame() {  
  
    if (resized) {
      resize();
      resized = false;
    }

    app.renderer.render(app.stage);
  }

  function resize() {

    vw = window.innerWidth;
    vh = window.innerHeight;

    app.renderer.resize(vw, vh);

    // svgScale = Math.min(vw / viewBox.width, vh / viewBox.height);
    svgScale = Math.min(vw / 450, vh / 150);

    // log("SCALE", svgScale)
    
    // svgTexture.scale = svgScale

    // updateSprites()
    
    svgTexture.sourceScale = svgScale;
    
    for (let key of frameKeys) {
      
      const frame = svgFrames[key]
      frame.orig.sourceScale = svgScale;
      frame.sourceFrame.sourceScale = svgScale;
      frame.layoutFrame.sourceScale = svgScale;
    }
    
    for (let sprite of sprites) {
      sprite.update();
    }
    
    // container.onChildrenChange(0)
    
    // log("\n")
    // log("FRAME", sprites[10].texture.frame)
    // log("BASETEXUTRE", sprites[0].texture.baseTexture.width)
  }
}

function random(min, max) {
  if (max == null) { max = min; min = 0; }
  if (min > max) { var tmp = min; min = max; max = tmp; }
  return min + (max - min) * Math.random();
}

function randomInt(min, max) {
  if (max == null) { max = min; min = 0; }
  if (min > max) { var tmp = min; min = max; max = tmp; }
  return Math.floor(min + (max - min + 1) * Math.random());
}


// PIXI.svg = PixiSvg;
// log("PIXI SVG", PixiSvg)




//
// 
// ===========================================================================
class SvgFrameX {
  
  constructor(frame, private _sourceScale = 1) {
    
    const source = frameData.sourceSize;
    const layout = frameData.layoutSize;
    this.origSource = new PIXI.Rectangle(source.x, source.y, source.width, source.height);
    this.origLayout = new PIXI.Rectangle(layout.x, layout.y, layout.width, layout.height);
    this.source = this.origSource.clone();
    this.layout = this.origLayout.clone();
  }
  
  get sourceScale() {
    return this._sourceScale;
  }
  
  set sourceScale(scale) {
    this._sourceScale = scale;
  }
}




// //
// // SVG SPRITE SHEET
// // =========================================================================== 
// class SvgSpriteSheet {
  
//   // constructor(baseTexture) {
//   constructor(public baseTexture, public data, public resolutionFilename = null) {
    
//     // this.canvas = document.createElement("canvas");
//     // this.context = this.canvas.getContext("2d");    
//     // this.baseTexture = PIXI.BaseTexture.fromCanvas(this.canvas);
    
//     this.textures = {};
//   }
  
//   parse(callback) {
    
//     this._callback = callback;
//     this._processFrames();
//     this._parseComplete();
//   }
  
//   _processFrames() {
    
//     log("PROCESS FRAMES", this)
//   }
  
//   _parseComplete() {
    
//     const callback = this._callback;
//     this._callback = null;
//     // callback.call(this, this.textures);
//     callback.call(this, this.textures, this);
//   }
// }

// //
// // SVG SPRITE SHEET PARSER
// // =========================================================================== 
// function svgSpriteSheetParser(resource, next) {
  
//   const svgResourceName = `${resource.name}_svg`;
  
//   if (!resource.data 
//      || resource.type !== Resource.TYPE.JSON
//      || !resource.data.svgFrames
//      || this.resources[svgResourceName]) {
    
//     next();
//     return;
//   }
  
//   const loadOptions = {
//     crossOrigin: resource.crossOrigin,
//     loadType: Resource.LOAD_TYPE.IMAGE,
//     metadata: resource.metadata.imageMetadata,
//     parentResource: resource,
//   };

//   const resourcePath = PIXI.loaders.getResourcePath(resource, this.baseUrl);
  
//   this.add(svgResourceName, resourcePath, loadOptions, function onImageLoad(res) {
    
//     const spriteSheet = new SvgSpriteSheet(
//       res.texture.baseTexture,
//       resource.data,
//       resource.url
//     );
    
//     spriteSheet.parse(() => {
//       resource.spritesheet = spriteSheet;
//       resource.textures = spriteSheet.textures;
//       next();
//     });
//   });  
// }



// var loader = new PIXI.loaders.Loader(baseUrl)
//   .add("icons_image", "icons-source.svg?v=1")
//   .add("icons", "icons5.json?v=1")
//   .load(onAssetsLoaded)

// var baseTexture = PIXI.BaseTexture.fromImage(baseUrl + "icons-source.svg?v=1", undefined, undefined, 1)

// baseTexture.on("loaded", (a, b) => {
//   log("BASE TEXTURE", baseTexture)
//   log("BASE TEXTURE A", a)
//   log("BASE TEXTURE B", b)
// })

// var loader = PIXI.loader;
// loader.baseUrl = baseUrl;
// loader.addPixiMiddleware(() => svgSpriteSheetParser)

// log("LOADER", loader)

// loader
  // .use(svgSpriteSheetParser)






// BIT TWIDDLE
// Nearest power of two
// https://stackoverflow.com/questions/31997707/rounding-value-to-nearest-power-of-two
// https://stackoverflow.com/questions/26965171/fast-nearest-power-of-2-in-javascript
// =========================================================================== 
function nextPow2(v) {
  v += v === 0;
  --v;
  v |= v >>> 1;
  v |= v >>> 2;
  v |= v >>> 4;
  v |= v >>> 8;
  v |= v >>> 16;
  return v + 1;
}

function prevPow2(v) {
  v |= v >>> 1;
  v |= v >>> 2;
  v |= v >>> 4;
  v |= v >>> 8;
  v |= v >>> 16;
  return v - (v >>> 1);
}

function nearestPow2_1(x) {
  return 1 << 31 - Math.clz32(x);
}

function nearestPow2_2(x) {
  // return Math.pow(2, Math.floor(Math.log(x) / Math.log(2)));
  return Math.pow(2, Math.round(Math.log(x) / Math.log(2)));
}

function nearestPow2_3(x) {
  const next = nextPow2(x);
  const prev = next >> 1;
  return next - x <= x - prev ? next : prev; // ceil
  return next - x < x - prev ? next : prev; // floor
}

function nearestPow2_4(x) {
  
}

function pow2floor(v){
  v++;
  var p = 1;
  while (v >>= 1) {p <<= 1;}
  return p;
}

function pow2ceil(v){
  v--;
  var p = 2;
  while (v >>= 1) {p <<= 1;}
  return p;
}

// const runButton = document.querySelector("#run-button");

// runButton.addEventListener("click", startTests);

function startTests() {
  
  const num = 1000000;
  const val = 123456789;
  let startTime;
  
  log("\nStarting Tests")
  log("\n")
  
  startTime = performance.now()
  runner(nearestPow2_1, val, num)
  log("nearestPow2_1", performance.now() - startTime)
  
  startTime = performance.now()
  runner(nearestPow2_2, val, num)
  log("nearestPow2_2", performance.now() - startTime)
  
  startTime = performance.now()
  runner(nearestPow2_3, val, num)
  log("nearestPow2_3", performance.now() - startTime)
  
  startTime = performance.now()
  runner(pow2ceil, val, num)
  log("pow2ceil", performance.now() - startTime)
  
  startTime = performance.now()
  runner(pow2floor, val, num)
  log("pow2floor", performance.now() - startTime)
  
}



// var values = [3,6,8,9,12,15,100,500,750]
// var values = [3.3,6.3,8.3,9.3,12.3,15.3,100.3,500.3,750.3]

// for (let i = 0; i < values.length; i++) {
  
//   const value = values[i]
//   log("\n" + value)
//   log("nearestPow2_1", nearestPow2_1(value))
//   log("nearestPow2_2", nearestPow2_2(value))
//   log("nearestPow2_3", nearestPow2_3(value))
//   log("pow2ceil", pow2ceil(value))
//   log("pow2floor", pow2floor(value))
// }

// log("\n\n")
              
            
!
999px

Console