Google's Thanos Effect

by mmis1000

HTML

<script src="https://unpkg.com/[email protected]/dist/html2canvas.min.js"></script>
<div id="banner-message">
  <p>Hello World</p>
  <button type="button" class="js-alt">Change color</button>
</div>

<div id="effect"></div>

<div class="action">
  <button type="button" class="js-play">
    開始
  </button>
  <button type="button" class="js-reset">
    重置
  </button>
  <hr style="margin: 20px 0;" />
  <div class="line">
    <label for="layer-border">
      <input type="checkbox" id="layer-border" class="js-border" /> 顯示分層邊框
    </label>
  </div>
  <div class="line">
    <label for="layer-count">
      分層數
      <select id="layer-count" class="js-layer-count">
        <option>8</option>
        <option>16</option>
        <option>32</option>
        <option>64</option> 
        <option>128</option>
        <option>256</option>
        <option>512</option>
        <option>1024</option>
      </select>
    </label>
  </div>
</div>

CSS

body {
  background: #20262E;
  padding: 20px;
  font-family: Helvetica, sans-serif;
  overflow-x: hidden;
}

#banner-message {
  background: #fff;
  padding: 20px;
  font-size: 25px;
  text-align: center;
  transition: all 0.2s;
  margin: 0 auto;
  width: 300px;
}

button {
  background: #0084ff;
  border: none;
  border-radius: 5px;
  padding: 8px 14px;
  font-size: 15px;
  color: #fff;
}

#banner-message.alt {
  background: #0084ff;
  color: #fff;
  margin-top: 40px;
  width: 200px;
}

#banner-message.alt button {
  background: #fff;
  color: #000;
}

.action {
  margin-top: 1rem;
  text-align: center;
  color: #fff;
}

.action .line {
  margin: 15px 0;
}

#effect {
  position: absolute;
  text-align: center;
  pointer-events: none;
}

#effect canvas {
  position: absolute;
  left: 0;
  top: 0;
  transition: transform 1.5s ease-out, opacity 1.5s ease-out;
}

#effect.border canvas {
  box-shadow: 0 0 0 1px red;
}

JavaScript

$(() => {
  const LAYER_COUNT = 32;
  const TRANSITION_DURATION = 1.5;
  const TRANSITION_DELAY = 1.35;

  let currentLayerCount = LAYER_COUNT;

  const $target = $('#banner-message');
  const target = $target[0];

  const $effect = $('#effect');

  const hideTarget = () => {
    $target.css({
      'transition': `opacity ${TRANSITION_DURATION} ease`,
      opacity: 0
    });
    delay(1e3 * TRANSITION_DURATION).then(() => {
      $target.css('visibility', 'hidden');
    });
  };

  const showTarget = () => {
    $target.css({
      opacity: 1,
      visibility: 'visible',
      'transition': `opacity 1s ease`
    });
  };

  // bind events
  $('.js-play').on('click', function() {
    play(currentLayerCount);
  });
  $('.js-reset').on('click', function() {
    var $canvas = $("canvas")
    $canvas.css({
      transform: `rotate(0deg) translate(0px, 0px) rotate(0deg)`,
      opacity: 1
    });
    setTimeout(function() {
      showTarget();
      setTimeout(function() {
        $canvas.css({
          opacity: 0,
          'transition': `opacity 1s ease`
        })
        setTimeout(function() {
          $canvas.remove()
        }, 1000)
      }, 1000)
    }, 3000)
  });
  $('.js-alt').on('click', function($evt) {
    $target.toggleClass('alt', $evt.target.checked);
  });
  $('.js-border').on('change', function($evt) {
    $effect.toggleClass('border', $evt.target.checked);
  });
  $('.js-layer-count').on('change', function($evt) {
    currentLayerCount = Number($evt.target.value);
  });
  $('.js-layer-count').val(currentLayerCount);

  function play(layerCount) {
    showTarget();

    const bRect = target.getBoundingClientRect();
    $effect.css({
      left: bRect.left,
      top: bRect.top,
      width: bRect.width,
      height: bRect.height
    });

    html2canvas(target, {
        backgroundColor: null,
      })
      .then(canvas => {
        const context = canvas.getContext('2d');
        const {
          width,
          height
        } = canvas;

...