UNPKG

roseworx

Version:

Front end css and js framework

747 lines (700 loc) 21.9 kB
import { rwxCore, rwxCanvasComponent } from '../rwxCore'; import rwxMath from '../helpers/rwxMathHelpers'; import rwxMisc from '../helpers/rwxMiscHelpers'; import rwxCanvas from '../helpers/rwxCanvasHelpers'; import { rwxAnimation, rwxAnimationChain, rwxAnimate } from '../modules/rwxAnimation'; class rwxPhotoTiles extends rwxCore { constructor() { super({selector:'[rwx-phototile]', canHaveManualControl:false, resource: 'rwxPhotoTiles'}); this.defaultEffect = 'random'; this.defaultTimeout = 5; } execute(el) { const effect = this.checkAttributeOrDefault(el, 'data-rwx-phototile-effect', this.defaultEffect); const auto = this.checkAttributeForBool(el, 'data-rwx-phototile-auto'); const autoTimeout = this.checkAttributeOrDefault(el, 'data-rwx-phototile-auto-timeout', this.defaultTimeout); const noThumbnails = this.checkAttributeForBool(el, 'data-rwx-phototile-no-thumbnails'); return new rwxPhotoTile(el, effect, auto, autoTimeout, noThumbnails); } goToTile(id, photoNumber, effect) { if(!this.validateParameter(photoNumber, 'number', 'goToTile'))return; const IME = this.getIME(id); if(IME && !IME.fx.includes(effect)){this.error(`${effect} is not a valid effect, picking random one.`)} IME && IME.changeBackground(photoNumber, effect, false, false); } } class rwxPhotoTile extends rwxCanvasComponent { constructor(el, effect, auto, autoTimeout, noThumbnails) { super({element: el, enableAnimationLoop: true, enableResizeDebounce: true}); this.photos = [...el.children]; if(this.photos.length == 0)return; this.effectInit = effect; this.autoLoop = this.autoLoop.bind(this); this.counter = 0; this.firstblood = true; this.numberOfTiles = 100; this.numberOfXTiles = Math.sqrt(this.numberOfTiles); this.numberOfYTiles = Math.sqrt(this.numberOfTiles); this.tileMatrix = []; this.animeCounter = []; this.maxTimeout = 15; this.fx = [ 'bubble', 'spin', 'pixelated', 'slideRandom', 'slideLeft', 'slideRight', 'slideUp', 'slideDown', 'puzzle' ]; if(!this.fx.includes(effect) && effect !== "random") { this.error(`${effect} isn't a valid effect, possible effects are ${this.fx.join(', ')}.`) } this.deferImageLoad().then(()=>{ this.el.classList.add('loaded'); this.createCanvas(); this.calculateSize(); this.photoLoop(noThumbnails); this.changeBackground(1, this.effectInit); this.calculateRandomTilePairing(); if(auto) { this.autoLoopInterval = autoTimeout * 60; this.autoLoop(); } }); } resize() { this.calculateSize(); this.changeBackground(this.currentPhotoNumber, 'none', true); } isPhotoNumberInRange(number) { return (number > this.photos.length || number < 0) ? 1 : number; } cleanUp() { this.stopLoop = true; this.cloned.map((img)=>{ this.el.appendChild(img); return false; }); } autoLoop() { if(this.stopLoop)return; if(this.counter >= this.autoLoopInterval) { let done = this.changeBackground(this.currentPhotoNumber+1, this.effectInit); this.counter = 0; } else { this.counter+=1; } window.requestAnimationFrame(this.autoLoop); } photoLoop(noThumbnails) { this.container = document.createElement('div'); this.container.classList.add('rwx-phototile-container'); this.cloned = this.photos.map((img)=>img.cloneNode()); this.photos.map((img, i)=>{ rwxMisc.convertToButton(img); img.addEventListener('keydown', (ev)=>{ if(rwxMisc.isKeyboardClick(ev)) { ev.preventDefault(); this.changeBackground(i+1, this.effectInit); } else if(ev.keyCode == 37){ this.photos[i == 0 ? this.photos.length-1 : i-1].focus(); } else if(ev.keyCode == 39){ this.photos[i+1 == this.photos.length ? 0 : i+1].focus(); } }); img.addEventListener('click', ()=>{this.changeBackground(i+1, this.effectInit)}); if(noThumbnails){img.style.display = "none"} this.container.appendChild(img); return; }); this.addElement(this.el, this.container); } deferImageLoad() { const images = this.photos.filter((p)=>p.nodeName==="IMG"); const complete = images.map((i)=>i.complete).every((e)=>e) if(complete) { return new Promise((resolve, reject)=>{resolve()}) } return new Promise((resolve, reject)=>{ let imageLoaded = []; images.map((p,i)=>p.onload = ()=>{ imageLoaded.push(i); if(imageLoaded.length===images.length) { resolve(); } } ) }) } calculateSize() { let heights = [] let widths = []; this.photos.map((img, i)=>{ if(img.nodeName != "IMG") return; heights.push(img.naturalHeight); widths.push(img.naturalWidth); return false; }); let maxWidth = Math.max(...widths); let maxHeight = Math.max(...heights); let rect = this.el.getBoundingClientRect(); if(maxWidth > rect.width){maxWidth = rect.width;} if(maxWidth < maxHeight){maxHeight = maxWidth;} this.canvasWidth = maxWidth; this.canvasHeight = maxHeight; this.sizeCanvas(); } changeBackground(photoNumber, effect, force=false, allowReloop=true) { if(!force) { if(this.currentPhotoNumber === photoNumber || !this.stopAnimation)return; } if(!allowReloop) { if(photoNumber > this.photos.length || photoNumber < 0) return; } photoNumber = this.isPhotoNumberInRange(photoNumber) this.currentPhotoNumber = photoNumber; let node = this.photos[photoNumber-1]; let value, matrix, changeType; let isImg = node.nodeName == "IMG"; if(isImg) { value = node; matrix = this.calculateImageMatrix(value); changeType = "img"; } else { value = getComputedStyle(node)['backgroundColor']; matrix = this.calculateColorMatrix(); changeType = "color"; } this.effect = this.fx.includes(effect) ? effect : this.fx[rwxMath.randomInt(0, this.fx.length-1)]; if(effect == 'none'){this.effect = 'none';} for(let [index, t] of matrix.entries()) { let timeout = rwxMath.randomInt(0, this.maxTimeout); let obj = { sx:t.sx, sy:t.sy, sw:t.sw, sh:t.sh, dx:t.dx, dy:t.dy, dw:t.dw, dh:t.dh, changeType: changeType, timeout: timeout, value: value } if(this.firstblood) { let tile = new Tile(this.c, obj.value, obj.changeType, obj.sx, obj.sy, obj.sw, obj.sh, obj.dx, obj.dy, obj.dw, obj.dh, obj.timeout, this.pixelRatio); this.tileMatrix.push(tile); tile[changeType](); } else { this.tileMatrix[index].nextMatrix = obj; if(index == matrix.length-1) { this.resetAnimation(); this.startAnimation(); } } } this.firstblood = false; } calculateRandomTilePairing() { const cloned = rwxMisc.safeCloneArray(this.tileMatrix); const firsthalf = cloned.slice(0, this.numberOfTiles/2); const secondhalf = cloned.slice(this.numberOfTiles/2); rwxMisc.shuffleArray(firsthalf); rwxMisc.shuffleArray(secondhalf); firsthalf.map((t, i)=>{ t.tileLink = secondhalf[i]; secondhalf[i].tileLink = t; t.randomTileLink = rwxMisc.randomValueFromArray(this.tileMatrix); secondhalf[i].randomTileLink = rwxMisc.randomValueFromArray(this.tileMatrix); return false; }); } calculateColorMatrix() { let colorMatrix = []; let xincrement = this.width / this.numberOfXTiles; let yincrement = this.height / this.numberOfYTiles; for(let y=0;y<this.numberOfYTiles;y++) { for(let x=0;x<this.numberOfXTiles;x++) { colorMatrix.push({ sx: (x*xincrement), sy: (y*yincrement), sw: xincrement, sh: yincrement, dx: (x*xincrement), dy: (y*yincrement), dw: xincrement, dh: yincrement, }); } } return colorMatrix; } calculateImageMatrix(img) { let sx = 0; let sw = img.naturalWidth; let sy = 0; let sh = img.naturalHeight; if(img.naturalWidth > this.width) { sx = (img.naturalWidth - this.width) / 2; sw = this.width; } if(img.naturalHeight > this.height) { sy = (img.naturalHeight - this.height) / 2; sh = this.height; } let xincrement = sw / this.numberOfXTiles; let yincrement = sh / this.numberOfYTiles; let xOffset = img.naturalWidth < this.width ? (this.width-img.naturalWidth)/2 : 0; let yOffset = img.naturalHeight < this.height ? (this.height-img.naturalHeight)/2 : 0; let imageMatrix = []; for(let y=0;y<this.numberOfYTiles;y++) { for(let x=0;x<this.numberOfXTiles;x++) { imageMatrix.push({ sx: sx + (x*xincrement), sy: sy + (y*yincrement), sw: xincrement, sh: yincrement, dx: (x*xincrement) + xOffset, dy: (y*yincrement) + yOffset, dw: xincrement, dh: yincrement }); } } return imageMatrix; } resetAnimation() { this.stopAnimation = true; this.animeCounter = []; this.tileMatrix.map((t)=>{t.reset();t.animation=this.effect;}); } animate() { for(let [index,tile] of this.tileMatrix.entries()) { if(tile.animeDone && !this.animeCounter.includes(tile.uniqueID)) { this.animeCounter.push(tile.uniqueID); } tile[this.effect](); } if(this.animeCounter.length == this.tileMatrix.length) { // run custom events if any this.resetAnimation(); } } } function Tile(c, value, changeType, sx, sy, sw, sh, dx, dy, dw, dh, timeout, pixelRatio) { this.uniqueID = rwxMisc.uniqueId(); this.timeoutCounter = 0; this.duration = 500; this.slideDirections = ['slideLeft', 'slideRight', 'slideUp', 'slideDown']; this.scaleFactor = 2; Object.assign(this, {c, value, changeType, sx, sy, sw, sh, dx, dy, dw, dh, timeout, pixelRatio}); this.buildAnimations = function() { if(this.animation==="puzzle") { this.direction = rwxMath.randomInt(0,1); this.puzzleAnimation = new rwxAnimationChain({ sequence: [ { from: 0, to: 1, duration: this.duration, easing: 'linear', complete: ()=>{ this.initdx = this.dx; this.initdy = this.dy; this.initdw = this.dw; this.initdh = this.dh; window.requestAnimationFrame(()=>{ this.value = this.tileLink.nextMatrix.value; this.changeType = this.tileLink.nextMatrix.changeType; this.sw = this.tileLink.nextMatrix.sw; this.sx = this.tileLink.nextMatrix.sx; this.sh = this.tileLink.nextMatrix.sh; this.sy = this.tileLink.nextMatrix.sy; }) } }, { from: 0, to: 1, duration: this.duration, easing: 'linear', } ], complete: ()=>this.animeDone=true }) } if(this.animation==="bubble") { this.bubbleAnimation = new rwxAnimationChain({ sequence: [ { from:this.initradius, to:0, easing: 'linear', duration: this.duration, complete: ()=>{ this.switch(); } }, { to: ()=>{return (this.getCenterX() - this.nextMatrix.dx + 10)}, easing: 'linear', duration: this.duration } ], complete: ()=>this.animeDone=true }); } if(this.animation==="spin") { this.spinAnimation = new rwxAnimationChain({ sequence:[ { from: [this.initdw, this.initdh, this.initdx, this.initdy], to: [(this.initdw/this.scaleFactor), (this.initdh/this.scaleFactor), (this.initdx+(this.initdw/this.scaleFactor)/2), (this.initdy+(this.initdh/this.scaleFactor)/2)], easing: 'easeOutQuart', duration: this.duration }, { from: 0, to: 1, easing: 'easeInOutQuad', duration: this.duration }, { from: 0, to: 1, easing: 'easeInQuart', duration: this.duration } ], complete: ()=>this.animeDone=true }); } if(this.slideDirections.includes(this.animation) || this.animation==="slideRandom") { this.slideDirection = this.animation; if(this.animation==="slideRandom") { this.slideDirection = this.slideDirections[rwxMath.randomInt(0,this.slideDirections.length-1)]; } this.slideAnimation = new rwxAnimationChain({ sequence: [ { from: 0, to:1, easing: 'easeInQuad', duration: this.duration, complete: ()=>window.requestAnimationFrame(()=>this.switch()) }, { from:0, to:1, easing: 'easeOutQuad', duration:this.duration } ], complete: ()=>this.animeDone=true }) } if(this.animation === "pixelated") { this.pixelatedAnimation = new rwxAnimationChain({ sequence: [ { from: 1, to: 0, duration: this.duration, easing: 'easeInOutQuint', complete: ()=>this.switch() }, { to:1, duration: this.duration, easing: 'easeInOutQuint' } ], complete: ()=>this.animeDone=true }) } } this.getCenterX = ()=>{ return this.centerX; } this.reset = function() { this.timeoutCounter = 0; this.animeDone = false; this.switched = false; this.initialised = false; delete this.bubbleAnimation; delete this.spinAnimation; delete this.slideAnimation; delete this.pixelatedAnimation; delete this.puzzleAnimation; } this.initialise = function() { if(!this.initialised) { this.initdx = this.dx; this.initdy = this.dy; this.initdh = this.dh; this.initdw = this.dw; this.initsx = this.sx; this.initsy = this.sy; this.initsh = this.sh; this.initsw = this.sw; this.initcenterX = this.dx + (this.dw*0.5); this.initcenterY = this.dy + (this.dh*0.5); this.initradius = this.initcenterX - this.dx + 10; // + 10 bit of breathign room this.initialised = true; this.buildAnimations(); } } this.switch = function(translate=true) { if(!this.switched) { this.value = this.nextMatrix.value; this.changeType = this.nextMatrix.changeType; this.sw = this.nextMatrix.sw; this.sx = this.nextMatrix.sx; this.sh = this.nextMatrix.sh; this.sy = this.nextMatrix.sy; this.centerX = this.nextMatrix.dx + (this.nextMatrix.dw*0.5); this.centerY = this.nextMatrix.dy + (this.nextMatrix.dh*0.5); if(translate) { this.dw = this.nextMatrix.dw; this.dx = this.nextMatrix.dx; this.dh = this.nextMatrix.dh; this.dy = this.nextMatrix.dy; } this.switched = true; } } this.puzzle = function() { this.initialise(); if(this.timeoutCounter >= this.timeout && !this.animeDone) { this.puzzleAnimation.animate([ (val)=>{ if(this.direction === 1) { this.dx = rwxAnimate.fromToCalc(this.initdx, this.randomTileLink.nextMatrix.dx, val); } else { this.dy = rwxAnimate.fromToCalc(this.initdy, this.randomTileLink.nextMatrix.dy, val); } this.dw = rwxAnimate.fromToCalc(this.initdw, this.randomTileLink.nextMatrix.dw, val); this.dh = rwxAnimate.fromToCalc(this.initdh, this.randomTileLink.nextMatrix.dh, val); }, (val)=>{ this.dx = rwxAnimate.fromToCalc(this.initdx, this.tileLink.nextMatrix.dx, val); this.dy = rwxAnimate.fromToCalc(this.initdy, this.tileLink.nextMatrix.dy, val); this.dw = rwxAnimate.fromToCalc(this.initdw, this.tileLink.nextMatrix.dw, val); this.dh = rwxAnimate.fromToCalc(this.initdh, this.tileLink.nextMatrix.dh, val); } ]) } this[this.changeType](); this.timeoutCounter++; } this.spin = function() { this.initialise(); if(this.timeoutCounter >= this.timeout && !this.animeDone) { this.spinAnimation.animate([ (w, h, x, y)=>{ this.dw = w; this.dh = h; this.dx = x; this.dy = y; }, (v)=>{ this.c.translate(this.initcenterX, this.initcenterY); this.c.rotate((v*360) * Math.PI / 180); this.c.translate(-this.initcenterX, -this.initcenterY); if(v > 0.5) { this.switch(false); } }, (v)=>{ this.dw += (this.nextMatrix.dw - this.dw)*v; this.dh += (this.nextMatrix.dh - this.dh)*v; this.dx += (this.nextMatrix.dx - this.dx)*v; this.dy += (this.nextMatrix.dy - this.dy)*v; } ]) } this[this.changeType](); this.c.setTransform(this.pixelRatio, 0, 0, this.pixelRatio, 0, 0); this.timeoutCounter++; } this.pixelated = function () { this.initialise(); if(this.timeoutCounter >= this.timeout && !this.animeDone) { this.pixelatedAnimation.animate([ (o)=>c.globalAlpha=o, (o)=>c.globalAlpha=o ]) } this[this.changeType](); this.timeoutCounter++; } this.slideRandom = ()=>this.slide(); this.slideLeft = ()=>this.slide(); this.slideRight = ()=>this.slide(); this.slideUp = ()=>this.slide(); this.slideDown = ()=>this.slide(); this.slide = function () { this.initialise(); if(this.timeoutCounter >= this.timeout && !this.animeDone) { this.slideAnimation.animate([ (val)=>{ if(this.slideDirection == "slideLeft" || this.slideDirection == "slideRight") { this.dw = rwxAnimate.fromToCalc(this.initdw, 0, val); this.sw = rwxAnimate.fromToCalc(this.initsw, 0, val); if(this.slideDirection == "slideRight") { this.dx = rwxAnimate.fromToCalc(this.initdx, (this.initdx+this.initdw), val); this.sx = rwxAnimate.fromToCalc(this.initsx, (this.initsx+this.initsw), val); } } if(this.slideDirection == "slideUp" || this.slideDirection == "slideDown") { this.dh = rwxAnimate.fromToCalc(this.initdh, 0, val); this.sh = rwxAnimate.fromToCalc(this.initsh, 0, val); if(this.slideDirection == "slideDown") { this.dy = rwxAnimate.fromToCalc(this.initdy, (this.initdy+this.initdh), val); this.sy = rwxAnimate.fromToCalc(this.initsy, (this.initsy+this.initsh), val); } } }, (val2)=>{ if(this.slideDirection == "slideLeft" || this.slideDirection == "slideRight") { this.dw = rwxAnimate.fromToCalc(0, this.nextMatrix.dw, val2); this.sw = rwxAnimate.fromToCalc(0, this.nextMatrix.sw, val2); if(this.slideDirection == "slideRight") { this.dx = rwxAnimate.fromToCalc((this.nextMatrix.dx + this.nextMatrix.dw), this.nextMatrix.dx, val2); this.sx = rwxAnimate.fromToCalc((this.nextMatrix.sx + this.nextMatrix.sw), this.nextMatrix.sx, val2); } } if(this.slideDirection == "slideUp" || this.slideDirection == "slideDown") { this.dh = rwxAnimate.fromToCalc(0, this.nextMatrix.dh, val2); this.sh = rwxAnimate.fromToCalc(0, this.nextMatrix.sh, val2); if(this.slideDirection == "slideDown") { this.dy = rwxAnimate.fromToCalc((this.nextMatrix.dy + this.nextMatrix.dh), this.nextMatrix.dy, val2); this.sy = rwxAnimate.fromToCalc((this.nextMatrix.sy + this.nextMatrix.sh), this.nextMatrix.sy, val2); } } } ]); } this[this.changeType](); this.timeoutCounter++; } this.bubble = function() { this.initialise(); if(this.timeoutCounter >= this.timeout && !this.animeDone) { this.bubbleAnimation.animate([ (r)=>this.radius=r, (r)=>this.radius=r ]) c.save(); c.beginPath(); c.arc(this.centerX||this.initcenterX, this.centerY||this.initcenterY, this.radius, 0, Math.PI*2); c.clip(); c.closePath(); } this[this.changeType](); c.restore(); this.timeoutCounter++; } this.color = function() { c.beginPath(); c.fillStyle = this.value; c.rect(this.dx+1, this.dy+1, this.dw+1, this.dh+1); c.fill(); c.closePath(); } this.none = function() { this.initialise(); this.dw = this.nextMatrix.dw; this.dh = this.nextMatrix.dh; this.dx = this.nextMatrix.dx; this.dy = this.nextMatrix.dy; this.sx = this.nextMatrix.sx; this.sy = this.nextMatrix.sy; this.sw = this.nextMatrix.sw; this.sh = this.nextMatrix.sh; this[this.changeType](); this.animeDone = true; } this.img = function() { c.drawImage(this.value, this.sx, this.sy, this.sw, this.sh, this.dx, this.dy, this.dw, this.dh); } } export default new rwxPhotoTiles();