DEV Community

Cover image for Use front-end technology to achieve partial flow effect of static pictures
dragonir
dragonir

Posted on

Use front-end technology to achieve partial flow effect of static pictures

Disclaimer: The graphic and model materials involved in this article are only for personal study, research and appreciation. Please do not re-modify, illegally spread, reprint, publish, commercialize, or conduct other profit-making activities.

Background

If you have played ๐ŸŽฎ ใ€ŠKing of Gloryใ€‹ , ใ€ŠOnmyojiใ€‹ and other mobile games, you must have noticed its startup animation, skin vertical drawing cards and other scenes, often using static A simple animation of the base image and local liquid flow effect. These flow animations may appear in the slow-flowing water flow ๐ŸŒŠ, the flag fluttering in the wind ๐ŸŽ , the game character sleeve ๐Ÿงœโ€โ™€๏ธ, Cloud, rain, fog weather effects that slow down with time โ›… etc. This transition effect not only saves the cost of developing full-scale animations, but also makes the game screen more enthusiastic, adventurous, ๐Ÿ’ฐ, and advanced, and it is also easier to attract players.

This paper uses front-end development technology, combined with SVG and CSS to achieve a similar liquefaction flow effect. Knowledge in this article๏ผš mask-image ใ€ feTurbulence feDisplacementMap ใ€ filter ใ€ canvas drawing method, TimelineMax animation and input[type=file] local image resource loading, etc.

Effect

Let's take a look at the implementation effect first. The following examples and the ๐Ÿ‘† article Banner figure are all applied with the liquid flow animation effect generated by the content of this article. Since GIF the image compression is serious, the animation effect does not look very smooth ๐Ÿ™ƒ , you may wish to experience the effect yourself through the following demo page link and generate your own legend , Collection Skin vertical painting ๐Ÿ˜….

๐ŸŒ€ Mist Diffusion The Legend of Zelda: Breath of the Wild

example_0

๐Ÿ’ƒ Fluttering Diao Chan: Cat Shadow Phantom Dance

example_1

๐ŸŒ… Huguang Wave

example_2

๐Ÿ”  Text Liquefaction

example_3

๐Ÿ“Œ Tips: the experience page is deployed in Gitpage the uploading image function is not really uploaded to the server, but will only be loaded into the browser locally, the page will not get any information, you can experience it with confidence , don't worry about privacy leaks.

Codepen

See the Pen
paint-heat-map
by dragonir (@dragonir)
on CodePen.

Accomplish

The page is mainly composed of 2 part, the top is used to load the picture, and you can draw the hotspot path by pressing the ๐Ÿ–ฑ mouse swipe to add a flow effect to the picture; the bottom is the control area , click the button ๐Ÿ”˜ ๆธ…้™ค็”ปๅธƒ , you can clear the drawn flowing animation effect, click the button ๐Ÿ”˜ ๅˆ‡ๆขๅ›พ็‰‡ to load the local picture.

step_0

๐Ÿ“Œ Note, there is also an invisible function, when you finish drawing, you can click ๐Ÿ–ฑ Right mouse button , and then choose to save the picture, the saved picture is the path we draw fluid animation Using this heat map and using the knowledge of CSS in this article, you can convert static pictures into dynamic pictures!

HTML page structure

#sketch element is mainly used to draw and load the drawing board of the flow effect heat map; #button_container is the button control area at the bottom of the page; filter svg element filter Filters to achieve liquid flow animation effects, including feTurbulence and feDisplacementMap filters.

<main id="sketch">
  <canvas id="canvas" data-img=""></canvas>
  <div class="mask">
    <div id="maskInner" class="mask-inner"></div>
  </div>
</main>
<section class="button_container">
  <button class="button">ๆธ…้™ค็”ปๅธƒ</button>
  <button class="button"><input class="input" type="file" id="upload">ไธŠไผ ๅ›พ็‰‡</button>
</section>
<svg>
  <filter id="heat" filterUnits="objectBoundingBox" x="0" y="0" width="100%" height="100%">
    <feTurbulence id="heatturb" type="fractalNoise" numOctaves="1" seed="2" />
    <feDisplacementMap xChannelSelector="G" yChannelSelector="B" scale="22" in="SourceGraphic" />
  </filter>
</svg>
Enter fullscreen mode Exit fullscreen mode

๐Ÿ’ก feTurbulence and feDisplacementMap

  • feTurbulence : The filter uses the Perlin noise function to create an image, which can be used to simulate artificial textures such as moirรฉ and marble.
  • feDisplacementMap : Map displacement filter that replaces pixel values from in to space in an image with pixel values from in2 to space from the image. That is, it can change the pixel position of elements and graphics. By traversing all the pixels of the original graphics, feDisplacementMap remaps and replaces a new position to form a new graphics. The mainstream application of this filter in the industry is to deform, distort, and liquefy graphics.

CSS styles

Then look at the implementation of the style, main element as the main container and the main pattern as the background image; canvas as the canvas to occupy the space of 100% ; .mask and .mask-inner are used to generate the effect of the hotspot path and the background image as shown below, which is achieved with the help of mask-image . Finally, in order to generate a dynamic flow effect, .mask-inner pass the filter:url(#heat) the previously generated svg as the source of the filter, and the follow-up will be in JavaScript By continuously modifying the properties of the svg filter, a liquid flow animation is generated.

main {
  position: relative;
  background-image: url('bg.jpg');
  background-size: cover;
  background-position: 100% 50%;
}
canvas {
  opacity: 0;
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
}
.mask {
  display: none;
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  mask-mode: luminance;
  mask-size: 100% 100%;
  backdrop-filter: hard-light;
  mask-image: url('mask.png');
}
.mask-inner {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  background: url('bg.jpg') 0% 0% repeat;
  background-size: cover;
  background-position: 100% 50%;
  filter: url(#heat);
  mask-image: url('mask.png')
}
Enter fullscreen mode Exit fullscreen mode

step_1

๐Ÿ’ก mask-image

mask-image CSS attribute is used to set the image of the mask layer on the element.

Syntax :

// The default, a transparent black image layer, i.e. no mask layer.
mask-image: none;
// <mask-source><mask> or CSS image url
mask-image: url(masks.svg#mask1);
// <image> image as mask
mask-image: linear-gradient(rgba(0, 0, 0, 1.0), transparent);
mask-image: image(url(mask.png), skyblue);
// multiple values
mask-image: image(url(mask.png), skyblue), linear-gradient(rgba(0, 0, 0, 1.0), transparent);
// global value
mask-image: inherit;
mask-image: initial;
mask-image: unset;
Enter fullscreen mode Exit fullscreen mode

Compatibility :

step_2

โšก This feature is still under development in some browsers and requires a browser prefix to be compatible with different browsers.

JavaScript methods

โ‘  Draw a heat map

Listen for mouse movement and click events, and draw wave path hotspots on canvas.

var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');
var sketch = document.getElementById('sketch');
var sketchStyle = window.getComputedStyle(sketch);
var mouse = { x: 0, y: 0 };

canvas.width = parseInt(sketchStyle.getPropertyValue('width'));
canvas.height = parseInt(sketchStyle.getPropertyValue('height'));
canvas.addEventListener('mousemove', e => {
  mouse.x = e.pageX - canvas.getBoundingClientRect().left;
  mouse.y = e.pageY - canvas.getBoundingClientRect().top;
}, false);

ctx.lineWidth = 40;
ctx.lineJoin = 'round';
ctx.lineCap = 'round';
ctx.strokeStyle = 'black';

canvas.addEventListener('mousedown', () => {
  ctx.beginPath();
  ctx.moveTo(mouse.x, mouse.y);
  canvas.addEventListener('mousemove', onPaint, false);
}, false);

canvas.addEventListener('mouseup', () => {
  canvas.removeEventListener('mousemove', onPaint, false);
}, false);

var onPaint = () => {
  ctx.lineTo(mouse.x, mouse.y);
  ctx.stroke();
  var url = canvas.toDataURL();
  document.querySelectorAll('div').forEach(item => {
    item.style.cssText += `
      display: initial;
      -webkit-mask-image: url(${url});
      mask-image: url(${url});
    `;
  });
};
Enter fullscreen mode Exit fullscreen mode

After the drawing is completed, you can right-click on the page to save the generated heat map of the wave path, and directly put the heat map that you are satisfied with drawing into CSS , you can add local wave effect to the picture you like, the following one The image is the fluctuating heat path map used on this example page.

step_3

โ‘ก Generate animation

In order to generate the real-time updated wave effect, this article uses TweenMax to achieve the same function by changing the value of the baseFrequency property of feTurbulence, you also can use other animation libraries or using requestAnimationFrame.

feTurb = document.querySelector('#heatturb');
var timeline = new TimelineMax({
  repeat: -1,
  yoyo: true
}),
timeline.add(
  new TweenMax.to(feTurb, 8, {
    onUpdate: () => {
      var bfX = this.progress() * 0.01 + 0.025,
        bfY = this.progress() * 0.003 + 0.01,
        bfStr = bfX.toString() + ' ' + bfY.toString();
      feTurb.setAttribute('baseFrequency', bfStr);
    }
  }),
0);
Enter fullscreen mode Exit fullscreen mode

โ‘ข Clear the canvas

Click the ๆธ…้™ค็”ปๅธƒ button to clear the drawn wave path, mainly by clearing the attribute value of the page element mask-image and clearing the canvas.

function clear() {
  document.querySelectorAll('div').forEach(item => {
    item.style.cssText += `
      display: none;
      -webkit-mask-image: none;
      mask-image: none;
    `;
  });
}

document.querySelectorAll('.button').forEach(item => {
  item.addEventListener('click', () => {
    ctx.clearRect(0, 0, canvas.width, canvas.height);
    clear();
  })
});
Enter fullscreen mode Exit fullscreen mode

โ‘ฃ Switch pictures

Click to switch the picture, you can load a local picture as the drawing base map, this function is to obtain the picture resource through input[type=file] , and then set it to a new one by modifying CSS Canvas background.

document.getElementById('upload').onchange = function () {
  var imageFile = this.files[0];
  var newImg = window.URL.createObjectURL(imageFile);
  clear();
  document.getElementById('sketch').style.cssText += `
    background: url(${newImg});
    background-size: cover;
    background-position: center;
  `;
  document.getElementById('maskInner').style.cssText += `
    background: url(${newImg});
    background-size: cover;
    background-position: center;
  `;
};
Enter fullscreen mode Exit fullscreen mode

At this point, all the functions have been realized. Let's make a startup page of your favorite wave skins or Odyssey mini game by this way ๐Ÿคฃ .

step_4

๐Ÿ“ฅ Source address: https://github.com/dragonir/paint-heat-map

Summarize

The knowledge points included in this article mainly include:

  • mask-image mask element
  • feTurbulence and feDisplacementMap svg filters
  • filter attribute
  • Canvas drawing method
  • TimelineMax animation
  • input[type=file] local image resource loading

If you want to know other front-end knowledge or other knowledge that is not described in detail in this article Web 3D development technology related knowledge, you can read my previous articles. Please indicate the original address and author when reprinting . If you think the article is helpful to you, don't forget to follow me ๐Ÿ‘ .

Appendix

refer

Top comments (3)

Collapse
 
thiagoow profile image
Thiago Silva Lopes

Gosh! It's even hard to believe that such an amazing effect can be done using only CSS & Three.js ๐Ÿ˜ฑ Loved the article buddy, looking forward to learn new things from Three.js with you! ๐Ÿฅณ

Collapse
 
dragonir profile image
dragonir

Thanks, I just created DEV account yeaterday and this is my first article, I'll post more useful article in the future ๐Ÿ˜Š

Collapse
 
thiagoow profile image
Thiago Silva Lopes

Love this one already, can't wait to see it! ๐Ÿค™๐Ÿผ