Morph images on hover Menu Portfolio with pixi.js

Using Pixi.js plugin displacement filter to "morph" images while hovering connected sidebar links. Done using html, css, jQuery, TweenMax and pixi.js. Demo and download available.

Demo Download

Author Karlo Videk
Hits
Created JUNE 29, 2018
License Open
Compatible browsers Chrome, Firefox, Safari

<script src='https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js'></script>
<script src='https://cdnjs.cloudflare.com/ajax/libs/gsap/1.20.3/TweenMax.min.js'></script>
<script src='https://cdnjs.cloudflare.com/ajax/libs/pixi.js/4.7.1/pixi.min.js'></script>

HTML Snippet

<h2 class="u-a7">
  Menu — Portfolio
</h2>
<div class="c-work">
  <ul class="c-work__list js-work">
    
    <!-- WORK ITEM -->
    <li class="c-work__item">
      <div class="c-work-item js-work-item" data-work-preview-id="0">
        <span class="c-work-item__number u-b6">
                            01
                        </span>
        <div class="c-work-item__title">
          <h3 class="u-a6">
            Hone Products
          </h3>
        </div>
        <span class="c-work-item__category u-b4">
                            <i>/</i>Website
                        </span>
      </div>
    </li>
    <!-- //WORK ITEM -->
    <!-- WORK ITEM -->
    <li class="c-work__item">
      <div class="c-work-item js-work-item" data-work-preview-id="1">
        <span class="c-work-item__number u-b6">
                            02
                        </span>
        <div class="c-work-item__title">
          <h3 class="u-a6">
            Urban Culture
          </h3>
        </div>
        <span class="c-work-item__category u-b4">
                            <i>/</i>Digital Presentation
                        </span>
      </div>
    </li>
    <!-- //WORK ITEM -->
    <!-- WORK ITEM -->
    <li class="c-work__item">
      <div class="c-work-item js-work-item" data-work-preview-id="2">
        <span class="c-work-item__number u-b6">
                            03
                        </span>
        <div class="c-work-item__title">
          <h3 class="u-a6">
            X — 6
          </h3>
        </div>
        <span class="c-work-item__category u-b4">
                            <i>/</i>Website
                        </span>
      </div>
    </li>
    <!-- //WORK ITEM -->
    <!-- WORK ITEM -->
    <li class="c-work__item">
      <div class="c-work-item js-work-item" data-work-preview-id="3">
        <span class="c-work-item__number u-b6">
                            04
                        </span>
        <div class="c-work-item__title">
          <h3 class="u-a6">
            Difference
          </h3>
        </div>
        <span class="c-work-item__category u-b4">
                            <i>/</i>Branding
                        </span>
      </div>
    </li>
    <!-- //WORK ITEM -->
    <!-- WORK ITEM -->
    <li class="c-work__item">
      <div class="c-work-item js-work-item" data-work-preview-id="4">
        <span class="c-work-item__number u-b6">
                            05
                        </span>
        <div class="c-work-item__title">
          <h3 class="u-a6">
            Cali-Hype
          </h3>
        </div>
        <span class="c-work-item__category u-b4">
                            <i>/</i>E-commerce
                        </span>
      </div>
    </li>
    <!-- //WORK ITEM -->
    <!-- WORK ITEM -->
    <li class="c-work__item">
      <div class="c-work-item js-work-item" data-work-preview-id="5">
        <span class="c-work-item__number u-b6">
                            06
                        </span>
        <div class="c-work-item__title">
          <h3 class="u-a6">
            Prada x Diesel
          </h3>
        </div>
        <span class="c-work-item__category u-b4">
                            <i>/</i>Website
                        </span>
      </div>
    </li>
    <!-- //WORK ITEM -->
  </ul>
  <div class="c-work-preview">
    <div class="c-work-preview__canvas js-work-preview-list">
      <div class="canvas js-work-preview-canvas" data-displacement-map="https://deghq.com/wordpress/bornfight/wp-content/themes/bf/static/ui/rock.png" style="background-image: url(https://deghq.com/wordpress/bornfight/wp-content/themes/bf/static/ui/rock.png);"></div>
    </div>
    <ul class="c-work-preview__list js-work-preview-list">
      <!-- WORK ITEM PREVIEW -->
      <li class="c-work-preview__item">
        <i class="c-work-preview__image js-work-preview" data-work-preview="https://deghq.com/wordpress/bornfight/wp-content/themes/bf/static/images/work-2.jpg" style="background-image: url(https://deghq.com/wordpress/bornfight/wp-content/themes/bf/static/images/work-2.jpg);"></i>
      </li>
      <!-- //WORK ITEM PREVIEW -->
      <!-- WORK ITEM PREVIEW -->
      <li class="c-work-preview__item">
        <i class="c-work-preview__image js-work-preview" data-work-preview="https://deghq.com/wordpress/bornfight/wp-content/themes/bf/static/images/work-3.jpg" style="background-image: url(https://deghq.com/wordpress/bornfight/wp-content/themes/bf/static/images/work-3.jpg);"></i>
      </li>
      <!-- //WORK ITEM PREVIEW -->
      <!-- WORK ITEM PREVIEW -->
      <li class="c-work-preview__item">
        <i class="c-work-preview__image js-work-preview" data-work-preview="https://deghq.com/wordpress/bornfight/wp-content/themes/bf/static/images/work-4.jpg" style="background-image: url(https://deghq.com/wordpress/bornfight/wp-content/themes/bf/static/images/work-4.jpg);"></i>
      </li>
      <!-- //WORK ITEM PREVIEW -->
      <!-- WORK ITEM PREVIEW -->
      <li class="c-work-preview__item">
        <i class="c-work-preview__image js-work-preview" data-work-preview="https://deghq.com/wordpress/bornfight/wp-content/themes/bf/static/images/work-5.jpg" style="background-image: url(https://deghq.com/wordpress/bornfight/wp-content/themes/bf/static/images/work-5.jpg);"></i>
      </li>
      <!-- //WORK ITEM PREVIEW -->
      <!-- WORK ITEM PREVIEW -->
      <li class="c-work-preview__item">
        <i class="c-work-preview__image js-work-preview" data-work-preview="https://deghq.com/wordpress/bornfight/wp-content/themes/bf/static/images/work-6.jpg" style="background-image: url(https://deghq.com/wordpress/bornfight/wp-content/themes/bf/static/images/work-6.jpg);"></i>
      </li>
      <!-- //WORK ITEM PREVIEW -->
      <!-- WORK ITEM PREVIEW -->
      <li class="c-work-preview__item">
        <i class="c-work-preview__image js-work-preview" data-work-preview="https://deghq.com/wordpress/bornfight/wp-content/themes/bf/static/images/work-7.jpg" style="background-image: url(https://deghq.com/wordpress/bornfight/wp-content/themes/bf/static/images/work-7.jpg);"></i>
      </li>
      <!-- //WORK ITEM PREVIEW -->
    </ul>
  </div>
</div>

CSS Code

@import url("https://fonts.googleapis.com/css?family=Raleway");
body {
  font-family: 'Raleway', sans-serif;
  line-height: 1em;
  background-color: #0d0d0d;
  color: #ffffff;
  overflow: hidden;
}

.u-b6 {
  margin: 0;
  font-size: 10px;
}

.u-b4 {
  margin: 0;
  font-size: 14px;
}

.u-a6 {
  margin: 0;
  margin-bottom: 0.6em;
  font-size: 36px;
}

.u-a7 {
  margin: 0;
  font-size: 64px;
  padding: 60px 60px 0 60px;
}

.c-work {
  width: 100%;
  padding: 60px;
  height: 80vh;
  display: flex;
  align-items: center;
  overflow: hidden;
}
.c-work__list {
  position: relative;
  z-index: 2;
}
.c-work__item {
  display: block;
}

.c-work-item {
  position: relative;
  display: inline-block;
  cursor: pointer;
  padding: 15px 0;
}
.c-work-item__number {
  position: absolute;
  display: inline-block;
  right: 100%;
  top: 25px;
  margin-right: 25px;
  pointer-events: none;
  transition: all 0.2s 0.1s cubic-bezier(0.25, 0.46, 0.45, 0.94);
  will-change: transform, opacity;
}
.c-work-item__title {
  transition: all 0.2s cubic-bezier(0.25, 0.46, 0.45, 0.94);
  will-change: transform;
}
.c-work-item__category {
  display: inline-block;
  transition: all 0.2s 0.1s cubic-bezier(0.25, 0.46, 0.45, 0.94);
  will-change: transform;
}
.c-work-item__category i {
  font-style: normal;
  display: inline-block;
  margin-right: 8px;
}
.c-work-item:hover .c-work-item__title {
  -webkit-transform: translateX(10px);
          transform: translateX(10px);
}
.c-work-item:hover .c-work-item__number {
  opacity: 0;
  -webkit-transform: translateX(20px);
          transform: translateX(20px);
}
.c-work-item:hover .c-work-item__category {
  -webkit-transform: translateX(10px);
          transform: translateX(10px);
}

.c-work-preview {
  position: fixed;
  width: 100%;
  height: 100vh;
  top: 0;
  right: 0;
  padding-right: 60px;
  pointer-events: none;
  z-index: 1;
}
.c-work-preview__canvas {
  display: block;
  position: absolute;
  top: 0;
  right: 60px;
  width: 41.6666666667%;
  height: 100%;
}
.c-work-preview__canvas .canvas {
  position: absolute;
  top: 50%;
  -webkit-transform: translateY(-50%);
          transform: translateY(-50%);
  width: 100%;
  left: 0;
  height: 0;
  padding-bottom: 56.25%;
  overflow: hidden;
  background-size: 0;
}
.c-work-preview__canvas canvas {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
}
.c-work-preview__list {
  display: block;
  position: absolute;
  top: 0;
  right: 60px;
  width: 41.6666666667%;
  height: 100%;
}
.c-work-preview__item {
  position: absolute;
  top: 50%;
  -webkit-transform: translateY(-50%);
          transform: translateY(-50%);
  width: 100%;
  left: 0;
  height: 0;
  padding-bottom: 56.25%;
  overflow: hidden;
}
.c-work-preview__image {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  background-size: cover;
  background-position: center;
  opacity: 0;
  visibility: hidden;
}

JavaScript

var _createClass = function () {function defineProperties(target, props) {for (var i = 0; i < props.length; i++) {var descriptor = props[i];descriptor.enumerable = descriptor.enumerable || false;descriptor.configurable = true;if ("value" in descriptor) descriptor.writable = true;Object.defineProperty(target, descriptor.key, descriptor);}}return function (Constructor, protoProps, staticProps) {if (protoProps) defineProperties(Constructor.prototype, protoProps);if (staticProps) defineProperties(Constructor, staticProps);return Constructor;};}();function _classCallCheck(instance, Constructor) {if (!(instance instanceof Constructor)) {throw new TypeError("Cannot call a class as a function");}}var WorkList = function () {
    function WorkList(options) {_classCallCheck(this, WorkList);
        var _defaults = {
            workItem: '.js-work-item',
            workItemPreviewList: '.js-work-preview-list',
            workItemImg: '.js-work-preview',

            //
            activeItemClass: 'is-active',

            //
            workCanvas: '.js-work-preview-canvas' };


        this.defaults = Object.assign({}, _defaults, options);

        if (this.getWorkItem().length > 0) {
            this.init();
            this.workItemHover(this.getWorkItem());
            this.workHover(this.getWorkItemPreviewList());
            this.initWorkCanvas();
        }
    }

    // region Getters

    /**
     *
     * @returns {*|jQuery|HTMLElement}
     */_createClass(WorkList, [{ key: 'getWorkItem', value: function getWorkItem()
        {
            return $(this.defaults.workItem);
        } }, { key: 'getWorkItemImg', value: function getWorkItemImg()

        {
            return $(this.defaults.workItemImg);
        } }, { key: 'getWorkItemPreviewList', value: function getWorkItemPreviewList()

        {
            return $(this.defaults.workItemPreviewList);
        } }, { key: 'getWorkCanvas', value: function getWorkCanvas()

        {
            return $(this.defaults.workCanvas);
        } }, { key: 'getCanvasEl', value: function getCanvasEl()

        {
            return $(this.defaults.workCanvas).find('canvas');
        }

        // endregion
    }, { key: 'init', value: function init()
        {
            console.log('WorkList init()');
        }

        /**
           *
           * @param workItem
           */ }, { key: 'workItemHover', value: function workItemHover(
        workItem) {var _this = this;
            workItem.on('mouseenter', function (e) {
                e.preventDefault();

                var workItemId = $(e.currentTarget).data('work-preview-id');

                _this.workHoverEnter(workItemId);
            });

            workItem.on('mouseleave', function () {
                _this.workHoverLeave();
            });
        }

        /**
           *
           * @param workContainer
           */ }, { key: 'workHover', value: function workHover(
        workContainer) {
            $(document).on('mousemove', function (ev) {
                var decimalX = ev.clientX / window.innerWidth - 0.5;
                var decimalY = ev.clientY / window.innerHeight - 0.5;

                TweenMax.to(workContainer, 0.4, {
                    x: 180 * decimalX,
                    y: 90 * decimalY,
                    ease: Power3.easeOut });

            });

            // SETTINGS
            var workItem = $(this.defaults.workItem);
            var hoverDuration = 0.4;
            var opacityLevel = 0.3;


            // CONTEXT SHIFTING
            // mouseenter
            $(document).on('mouseenter', this.defaults.workItem, function (ev) {
                ev.preventDefault();

                var notItem = workItem.not(ev.currentTarget);

                TweenMax.to(ev.currentTarget, hoverDuration, {
                    opacity: 1,
                    ease: Power3.easeOut });


                TweenMax.to(notItem, hoverDuration, {
                    opacity: opacityLevel,
                    x: 0,
                    ease: Power3.easeOut });

            });

            // mouseleave
            $(document).on('mouseleave', this.defaults.workItem, function (ev) {
                ev.preventDefault();

                var notItem = workItem.not(ev.currentTarget);

                TweenMax.to([ev.currentTarget, notItem], hoverDuration, {
                    opacity: 1,
                    ease: Power3.easeOut });

            });
        } }, { key: 'initWorkCanvas', value: function initWorkCanvas()

        {
            // CANVAS DIMENSIONS
            var canvasWidth = this.getWorkItemImg().innerWidth();
            var canvasHeight = this.getWorkItemImg().innerHeight();

            // CREATE PIXI APPLICATION
            var app = new PIXI.Application(
            canvasWidth,
            canvasHeight, {
                transparent: true });



            // APPEND CANVAS
            this.getWorkCanvas().append(app.view);

            // CREATE SLIDES CONTAINER
            this.slidesContainer = new PIXI.Container();
            app.stage.addChild(this.slidesContainer);

            // CREATE DISPLACEMENT MAP
            //const displacementMap = PIXI.Sprite.fromImage(this.getWorkCanvas().data('displacement-map'));

            var displacementMap = PIXI.Sprite.fromImage(this.getWorkCanvas().css('background-image').replace(/.*\s?url\([\'\"]?/, '').replace(/[\'\"]?\).*/, ''));

            // CREATE FILTER
            var filter = new PIXI.filters.DisplacementFilter(displacementMap);

            displacementMap.name = 'displacementMap';
            displacementMap.anchor.set(0.5);
            displacementMap.scale.set(1);
            displacementMap.position.set(canvasWidth / 2, canvasHeight / 2);

            app.stage.filterArea = app.screen;
            app.stage.filters = [filter];
            app.stage.addChild(displacementMap);

            // PIXI SPRITE ARRAY
            var _iteratorNormalCompletion = true;var _didIteratorError = false;var _iteratorError = undefined;try {for (var _iterator = this.getWorkItemImg()[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {if (window.CP.shouldStopExecution(0)) break;var spriteImage = _step.value;
                    //const texture = new PIXI.Texture.fromImage($(spriteImage).data('work-preview'));
                    var texture = new PIXI.Texture.fromImage($(spriteImage).css('background-image').replace(/.*\s?url\([\'\"]?/, '').replace(/[\'\"]?\).*/, ''));

                    var image = new PIXI.Sprite(texture);

                    image.name = 'workPreview';
                    image.alpha = 0;
                    image.width = canvasWidth;
                    image.height = canvasHeight;

                    this.slidesContainer.addChild(image);
                }

                // DISPLACE TIMELINE
                window.CP.exitedLoop(0);} catch (err) {_didIteratorError = true;_iteratorError = err;} finally {try {if (!_iteratorNormalCompletion && _iterator.return) {_iterator.return();}} finally {if (_didIteratorError) {throw _iteratorError;}}}this.displaceTl = new TimelineMax({
                paused: true });


            this.displaceTl.add('start').
            fromTo(this.getCanvasEl(), 0.4, {
                autoAlpha: 0 },
            {
                autoAlpha: 1,
                ease: Power4.easeOut },
            "start").
            fromTo(this.getCanvasEl(), 0.8, {
                scale: 1.25 },
            {
                scale: 1,
                ease: Power4.easeOut },
            "start").
            fromTo(
            filter.scale, 1.6, {
                x: 25,
                y: 75 },

            {
                x: 0,
                y: 0,
                ease: Power4.easeOut,
                onComplete: function onComplete() {

                } },
            "start");


            return [this.slidesContainer, this.displaceTl];
        } }, { key: 'workHoverEnter', value: function workHoverEnter(

        layerId) {var _this2 = this;
            // SET ALPHA OF HOVERED CASE PREVIEW
            TweenMax.to(this.slidesContainer.children[layerId], 0.4, {
                alpha: 1,
                ease: Power3.easeOut,
                onStart: function onStart() {
                    _this2.displaceTl.progress(0);
                    _this2.displaceTl.play();
                } });

        } }, { key: 'workHoverLeave', value: function workHoverLeave()

        {
            TweenMax.to(this.slidesContainer.children, 0.4, {
                alpha: 0,
                ease: Power3.easeOut });

        } }]);return WorkList;}();
new WorkList();

Preview

Morph images on hover Menu Portfolio with pixi.js preview

Advertisement

Google Matched Content...