Using canvas for image masking.
Image courtesy of Laura Aziz | Unsplash https://unsplash.com/photos/Zx5UKS3zC60
A Pen by Paul Mealy on CodePen.
Using canvas for image masking.
Image courtesy of Laura Aziz | Unsplash https://unsplash.com/photos/Zx5UKS3zC60
A Pen by Paul Mealy on CodePen.
<canvas id='stage'></canvas> | |
<h1>Mouse over image</h1> | |
<footer> | |
<a href="http://insidedown.com">Paul Mealy | InsideDown</a> | |
</footer> |
//*** SHIM *** | |
window.requestAnimFrame = (function() { | |
return window.requestAnimationFrame || | |
window.webkitRequestAnimationFrame || | |
window.mozRequestAnimationFrame || | |
window.oRequestAnimationFrame || | |
window.msRequestAnimationFrame || | |
function(callback) { | |
window.setTimeout(callback, 1000 / 60); | |
}; | |
})(); | |
(function() { | |
"use strict"; | |
//************ | |
//VARIABLES | |
//************ | |
var _Canvas; | |
let _frontImageSrc = 'http://insidedown.com/codepen/stock/mountain-tan.jpg'; | |
let _backImageSrc = 'http://insidedown.com/codepen/stock/mountain.jpg'; | |
let _frontImage; | |
let _backImage; | |
let _blackMask; | |
let _mouseX = 0; | |
let _mouseY = 0; | |
let _maskCount = 25; | |
let _tweenTime = 0.5; | |
let _pauseTime = 0.25; | |
let _delayTime = 0.08; | |
let _maskArray = []; | |
let _srcArray = ["http://insidedown.com/codepen/stock/newstain1.png", "http://insidedown.com/codepen/stock/newstain2.png", "http://insidedown.com/codepen/stock/newstain3.png"]; | |
//************ | |
//METHODS | |
//************ | |
function init() { | |
_Canvas = new Canvas({stage:document.getElementById('stage')}); | |
_backImage = new MaskedImage({src:_backImageSrc}); | |
_frontImage = new MaskedImage({src:_frontImageSrc}); | |
for(let i=0;i<_maskCount;i++){ | |
let ranSrc = _srcArray[Math.floor(Math.random() * _srcArray.length)]; | |
let mask = new MaskedImage({src:ranSrc, delay:i, width:300}); | |
_maskArray.push(mask); | |
} | |
addListeners(); | |
} | |
//************ | |
//EVENTS | |
//************ | |
function addListeners() { | |
_Canvas.el.addEventListener('mousemove', onCanvasMouseMove); | |
_Canvas.el.addEventListener('mouseout', onCanvasMouseOut); | |
} | |
function onCanvasMouseMove(event) { | |
_mouseX = event.pageX - $(this).offset().left; | |
_mouseY = event.pageY - $(this).offset().top; | |
} | |
function onCanvasMouseOut(event) { | |
} | |
function onEnterFrame() { | |
_Canvas.clearStage(); | |
drawStage(); | |
window.requestAnimFrame(onEnterFrame, 60); | |
} | |
function drawStage() { | |
_Canvas.context.save(); | |
for(let i=0;i<_maskCount;i++){ | |
let mask = _maskArray[i]; | |
mask.tweenDraw(); | |
} | |
//_blackMask.draw(_mouseX,_mouseY); | |
_Canvas.context.globalCompositeOperation = 'source-in'; | |
_backImage.draw(); | |
_Canvas.context.globalCompositeOperation = 'destination-over'; | |
_frontImage.draw(); | |
_Canvas.context.restore(); | |
} | |
//************ | |
//CLASSES | |
//************ | |
class MaskedImage { | |
constructor(options) { | |
this.hasImg = false; | |
this.img = new Image(); | |
this.empty = {scale:0, alpha:1, x:0, y:0}; | |
this.delay = options.delay; | |
this.rotation = Math.random() * 360; | |
this.width = options.width; | |
this.halfWidth = this.width/2; | |
this.img.src = options.src; | |
this.img.onload = function() { | |
this.hasImg = true; | |
if(this.delay){ | |
setTimeout(function() {this.scale();}.bind(this), this.delay*(_delayTime * 1000)); | |
} | |
this.draw(); | |
}.bind(this); | |
} | |
draw(x=0,y=0) { | |
if(this.hasImg) { | |
_Canvas.context.drawImage(this.img,x,y); | |
} | |
} | |
tweenDraw() { | |
if(this.hasImg) { | |
let curWidth = this.width * this.empty.scale; | |
_Canvas.context.save(); | |
_Canvas.context.globalAlpha = this.empty.alpha; | |
_Canvas.context.translate(this.empty.x, this.empty.y); | |
_Canvas.context.rotate(this.rotation * Math.PI / 180); | |
_Canvas.context.scale(1.5 * (curWidth/this.width), 1.5*(curWidth/this.width)); | |
_Canvas.context.translate(-this.empty.x, -this.empty.y); | |
_Canvas.context.drawImage(this.img,this.empty.x-this.halfWidth,this.empty.y-this.halfWidth); | |
_Canvas.context.globalAlpha = 1; | |
_Canvas.context.restore(); | |
} | |
} | |
scale() { | |
this.empty.x = _mouseX; | |
this.empty.y = _mouseY; | |
this.rotation = Math.random() * 360; | |
TweenMax.fromTo(this.empty, _tweenTime, {alpha:1, scale:0},{alpha:1, scale:1, onComplete:function(){ | |
setTimeout(this.fadeOut.bind(this), _pauseTime * 1000); | |
}.bind(this) | |
}); | |
} | |
fadeOut() { | |
TweenMax.to(this.empty, _tweenTime,{alpha:0, onComplete:this.scale.bind(this)}); | |
} | |
} | |
class Canvas { | |
constructor(options) { | |
this._stage = options.stage; | |
this._stageWidth = this._stage.width = window.innerWidth; | |
this._stageHeight = this._stage.height = window.innerHeight; | |
this._stageContext = this._stage.getContext('2d'); | |
} | |
// clear stage of current content | |
clearStage(options) { | |
if(typeof options === "undefined") { | |
this._stageContext.clearRect(0,0,this._stageWidth, this._stageHeight); | |
} | |
} | |
get width() { return this._stageWidth; } | |
get height() { return this._stageHeight; } | |
get el() {return this._stage; } | |
get context() {return this._stageContext; } | |
} //end Canvas class | |
init(); | |
onEnterFrame(); | |
})(); |
<script src="//cdnjs.cloudflare.com/ajax/libs/zepto/1.1.4/zepto.min.js"></script> | |
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/1.19.1/TweenMax.min.js"></script> |
@import url(https://fonts.googleapis.com/css?family=Lato:300,400,700, 400italic); | |
body { | |
background-color: #000; | |
height: 100%; | |
margin: 0; | |
overflow: hidden; | |
padding: 0; | |
width: 100%; | |
} | |
h1 { | |
color: #999; | |
font-family: 'Lato', helvetica, arial, sans-serif; | |
font-weight: normal; | |
font-size: 16px; | |
position: absolute; | |
text-align: center; | |
top: 0; | |
width: 100%; | |
} | |
canvas { | |
background-color: #111; | |
} | |
footer { | |
background: rgba(0,0,0,0.35); | |
border-radius: 0 10px 0px 0; | |
bottom: 0px; | |
color: #fff; | |
font-family: 'Lato', helvetica, arial, sans-serif; | |
font-size: 11px; | |
font-weight: 300; | |
padding: 8px 12px 8px 12px; | |
position: absolute; | |
text-shadow: -1px 1px #000000; | |
text-transform: uppercase; | |
z-index: 2; | |
} | |
footer a { | |
color: #fff; | |
text-decoration: none; | |
} | |
footer a:hover { | |
color: #ddd; | |
} |