Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save Frankie-666/ae17ce0d0e8d565b966d32d5217d46df to your computer and use it in GitHub Desktop.
Save Frankie-666/ae17ce0d0e8d565b966d32d5217d46df to your computer and use it in GitHub Desktop.
Displacementmap image transition

Displacementmap image transition

Scroll effect / Image transition test for a project using PIXI for Webgl abstraction. Effect is made by drawing the image, scaling x, applying threshold and using that as a mask.

A Pen by Felix Nielsen on CodePen.

License.

<!-- use your scroll-wheel -->
// ___ ___ ___ ___ ___
// /\ \ /\ \ /\__\ /\ \ |\__\
// /::\ \ /::\ \ /:/ / /::\ \ |:| |
// /:/\:\ \ /:/\:\ \ /:/ / /:/\:\ \ |:| |
// /::\~\:\ \ /::\~\:\ \ /:/ / /::\~\:\ \ |:|__|__
// /:/\:\ \:\__\ /:/\:\ \:\__\ /:/__/ /:/\:\ \:\__\ ____/::::\__\
// \/_|::\/:/ / \:\~\:\ \/__/ \:\ \ \/__\:\/:/ / \::::/~~/~
// |:|::/ / \:\ \:\__\ \:\ \ \::/ / ~~|:|~~|
// |:|\/__/ \:\ \/__/ \:\ \ /:/ / |:| |
// |:| | \:\__\ \:\__\ /:/ / |:| |
// \|__| \/__/ \/__/ \/__/ \|__|
// and use your scrollwheel
// relaxwearethegoodguys.com
// Much love to https://twitter.com/tokotokostudio for a fun exploration process
// Image credits --> https://www.behance.net/gallery/34853303/Destroying-nature-is-destroying-life
// Tweek the zoom values of each Transition object..
// Different images creates various effects..
var getBetweenColourByPercent = function (percent, highColor, lowColor) {
var r = highColor >> 16;
var g = highColor >> 8 & 0xFF;
var b = highColor & 0xFF;
r += ((lowColor >> 16) - r) * percent;
g += ((lowColor >> 8 & 0xFF) - g) * percent;
b += ((lowColor & 0xFF) - b) * percent;
var hex = (r << 16 | g << 8 | b);
return "#" + hex.toString(16);
};
var Application = (function () {
function Application() {
// should be overwritten
var _this = this;
this.introTweening = true;
this.colors = [0xffffff, 0xbcb7a4, 0xb4bec2, 0xbcb7a4, 0xb4bec2, 0xffffff];
this.infiniteScrollPos = 0;
this.direction = -1;
this.wheelSpeed = 0;
this.yRoller = 0;
this.progressBar = document.createElement("div");
this.progressBar.id = "progressbar";
document.body.appendChild(this.progressBar);
(window.TweenPlugin).activate([window.CSSPlugin]);
// create a renderer instance.
this.renderer = new PIXI.autoDetectRenderer(1400, 990, {
autoResize: true,
resolution: 1,
transparent: true
});
// this.renderer.backgroundColor = 0x000000;
// add the renderer view element to the DOM
document.body.appendChild(this.renderer.view);
this.stage = new PIXI.Container();
this.transitions = [];
this.transitions.push(new Transition({
renderer: this.renderer,
stage: this.stage,
image: "http://rwatggcdn.blob.core.windows.net/cdn/codepens/cp-5-01.jpg",
index: this.transitions.length,
zoom: 4
}));
this.transitions.push(new Transition({
renderer: this.renderer,
stage: this.stage,
image: "http://rwatggcdn.blob.core.windows.net/cdn/codepens/cp-5-02.jpg",
index: this.transitions.length,
zoom: 4
}));
this.transitions.push(new Transition({
renderer: this.renderer,
stage: this.stage,
image: "http://rwatggcdn.blob.core.windows.net/cdn/codepens/cp-5-01.jpg",
index: this.transitions.length,
zoom: 2
}));
this.transitions.push(new Transition({
renderer: this.renderer,
stage: this.stage,
image: "http://rwatggcdn.blob.core.windows.net/cdn/codepens/cp-5-02.jpg",
index: this.transitions.length,
zoom: 6
}));
this.render();
window.addEventListener("resize", this.resize.bind(this), false);
document.addEventListener('DOMMouseScroll', this.handleScroll.bind(this), false); // for Firefox
document.addEventListener('mousewheel', this.handleScroll.bind(this), false);
document.addEventListener("touchstart", this.onTouchStart.bind(this), false);
document.addEventListener("touchmove", this.onTouchMove.bind(this), false);
TweenLite.to(this, 1, {
yRoller: (window.innerWidth / 2),
ease: "easeOutCubic",
delay: 0.25,
onStart: function () { return _this.resize(null); },
onComplete: function () {
_this.introTweening = false;
}
});
}
Application.prototype.onTouchStart = function (event) {
event.preventDefault();
this.touchStartY = event.touches[0].pageY;
};
Application.prototype.onTouchMove = function (event) {
event.preventDefault();
this.wheelSpeed = (event.touches[0].pageY - this.touchStartY);
if (this.wheelSpeed > 0)
this.direction = 1;
else if (this.wheelSpeed < 0)
this.direction = -1;
this.touchStartY = event.touches[0].pageY;
};
Application.prototype.handleScroll = function (event) {
var normalized;
if (event.wheelDelta) {
normalized = (event.wheelDelta % 120 - 0) == -0 ? event.wheelDelta / 120 : event.wheelDelta / 12;
}
else {
var rawAmmount = event.deltaY ? event.deltaY : event.detail;
normalized = -(rawAmmount % 3 ? rawAmmount * 10 : rawAmmount / 3);
}
this.wheelSpeed = (normalized * 2);
if (this.wheelSpeed > 0)
this.direction = 1;
else if (this.wheelSpeed < 0)
this.direction = -1;
};
Application.prototype.render = function () {
// this.noiseFilter.padding += 1;
var _this = this;
if (!this.introTweening) {
this.wheelSpeed += (0 - this.wheelSpeed) * 0.05;
this.yRoller -= this.wheelSpeed;
}
var numItems = this.transitions.length - 1;
if (this.yRoller > window.innerWidth * numItems)
this.yRoller = (window.innerWidth * numItems);
if (this.yRoller < 0)
this.yRoller = 0;
var px = this.yRoller / window.innerWidth;
var progress = px * Application.overlap;
var p = px / numItems;
// colors
var l = this.colors.length - 1;
var colorProgress = p * l;
var colorIndex = Math.floor(colorProgress);
colorProgress = colorProgress - colorIndex;
var startColor = this.colors[colorIndex];
var endColor = this.colors[colorIndex + 1];
var color = getBetweenColourByPercent(colorProgress, startColor, endColor);
TweenLite.set(document.body, {
backgroundColor: color
});
if (this.progressBar) {
TweenLite.set(this.progressBar, {
force3D: true,
scaleX: p
});
}
for (var i = 0; i < this.transitions.length; i++) {
var element = this.transitions[i];
element.render(px);
}
this.renderer.render(this.stage);
requestAnimationFrame(function () { return _this.render(); });
};
Object.defineProperty(Application, "viewport", {
get: function () {
var ratio = 990 / 1400;
var w = window.innerWidth;
var h = window.innerWidth * ratio;
if (h > window.innerHeight) {
h = window.innerHeight;
w = (1400 / 990) * h;
}
return {
w: w,
h: h
};
},
enumerable: true,
configurable: true
});
Application.prototype.resize = function (event) {
var rect = Application.viewport;
this.renderer.view.style.width = rect.w + "px";
this.renderer.view.style.height = rect.h + "px";
this.renderer.view.style.left = ((window.innerWidth * 0.5) - (rect.w * 0.5)) + "px";
this.renderer.view.style.top = ((window.innerHeight * 0.5) - (rect.h * 0.5)) + "px";
// this.renderer.resize(rect.w, rect.h);
// for (var i = 0; i < this.transitions.length; i++) {
// var element: Transition = this.transitions[i];
// element.resize(rect.w, rect.h);
// }
};
Application.overlap = 0.6;
return Application;
}());
// starter code
window.startSingle = function () {
// should be overwritten with Master Application class
window.application = new Application();
};
var Transition = (function () {
function Transition(options) {
var _this = this;
this.progressTarget = 0;
this.renderer = options.renderer;
this.index = options.index;
this.zoom = options.zoom;
this.maskRenderer = new PIXI.autoDetectRenderer(this.renderer.width, this.renderer.height, {
autoResize: true
});
this.container = new PIXI.Container();
options.stage.addChild(this.container);
// add the mask for visual ref
// document.getElementById("page-content").appendChild(this.maskRenderer.view);
var texture;
// actual image
texture = PIXI.Texture.fromImage(options.image);
texture.baseTexture.on('loaded', function (baseTexture) {
var rect = Application.viewport;
_this.resize(rect.w, rect.h);
});
this.imageToBeMasked = new PIXI.Sprite(texture);
// mask image
texture = PIXI.Texture.fromImage("http://rwatggcdn.blob.core.windows.net/cdn/codepens/dm-02.jpg");
this.mask = new PIXI.Sprite(texture);
// displacement map, a copy of the original
texture = PIXI.Texture.fromImage(options.image);
this.imageDisplacementSprite = new PIXI.Sprite(texture);
this.container.addChild(this.imageDisplacementSprite);
this.maskDisplacementFilter = new PIXI.filters.DisplacementFilter(this.imageDisplacementSprite);
this.mask.filters = [this.maskDisplacementFilter];
this.maskToBeDrawTo_AKA_cache = new PIXI.Sprite();
this.imageToBeMasked.mask = this.maskToBeDrawTo_AKA_cache;
this.container.addChild(this.imageToBeMasked);
}
Transition.prototype.resize = function (w, h) {
this.hasResized = true;
var ratio = h / w;
var zoom = isNaN(this.zoom) ? 4 : this.zoom;
this.imageDisplacementSprite.width = w * zoom;
this.imageDisplacementSprite.height = h * zoom;
this.imageDisplacementSprite.texture.width = w * zoom;
this.imageDisplacementSprite.texture.height = h * zoom;
this.imageDisplacementSprite.x = (this.imageDisplacementSprite.width * -0.5) + (w * 0.5);
this.imageDisplacementSprite.y = (this.imageDisplacementSprite.height * -0.5) + (h * 0.5);
this.imageDisplacementSprite.texture.requiresUpdate = true;
this.mask.width = this.renderer.width;
this.mask.height = (this.renderer.height * 3) + 200; // + 200 because of distortion overlap
this.mask.texture.requiresUpdate = true;
this.imageToBeMasked.texture.width = w;
this.imageToBeMasked.texture.height = h;
this.imageToBeMasked.texture.requiresUpdate = true;
// center
this.container.pivot.x = w * 0.5;
this.container.pivot.y = h * 0.5;
this.container.x = w * 0.5;
this.container.y = h * 0.5;
this.maskToBeDrawTo_AKA_cache.texture.width = w;
this.maskToBeDrawTo_AKA_cache.texture.height = h;
this.maskToBeDrawTo_AKA_cache.texture.requiresUpdate = true;
// this.maskRenderer.resize(w, h);
};
Transition.prototype.render = function (progress) {
if (!this.hasResized)
return;
progress = progress - (this.index * Application.overlap); //overlap..
if (progress < -0.2 || progress > 1.2)
return;
var filterProgress = 1 - Math.abs((0.5 - (progress)) / 0.5);
var start = Math.min(1, progress / 0.1);
if (progress > 0.5)
start = 1;
var end = Math.min(1, Math.max(0, progress - 0.5) / 0.1);
if (progress < 0.5)
end = 1;
filterProgress *= start;
filterProgress *= end;
var scale = 1 + (progress * -0.2);
this.container.scale.x = scale;
this.container.scale.y = scale;
// manipulate this.mask displacement map filter
this.maskDisplacementFilter.scale.x = (filterProgress) * 600;
this.maskDisplacementFilter.scale.y = (filterProgress) * 600;
var overflowy = (progress * this.mask.height);
this.mask.y = overflowy * -1;
this.mask.y += progress * this.maskRenderer.height;
this.maskRenderer.render(this.mask);
// draw image from mask Sprite
this.maskToBeDrawTo_AKA_cache.texture = PIXI.Texture.fromCanvas(this.maskRenderer.view);
this.maskToBeDrawTo_AKA_cache.texture.update();
};
return Transition;
}());
// starter code
window.start = function () {
// should be overwritten with Master Application class
window.application = new Application();
};
if (document.readyState == "complete" || document.readyState == "interactive") {
if (!window.application)
window.start();
}
document.addEventListener("DOMContentLoaded", function (event) {
if (!window.application)
window.start();
}, false);
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/1.18.4/TweenMax.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/pixi.js/3.0.11/pixi.min.js"></script>
html, body
background #ffffff
width 100%
height 100vh
margin 0px
padding 0px
border 0px
outline 0px
overflow hidden
#progressbar
top 0px
left 0px
position fixed
width 100%
height 5px
background white
transform-origin 0% 0%
z-index 666
canvas
position absolute
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment