关于前端:使用前端技术实现静态图片局部流动效果-🌊

申明:本文波及图文和模型素材仅用于集体学习、钻研和观赏,请勿二次批改、非法流传、转载、出版、商用、及进行其余获利行为。

背景

如果你有玩过 🎮 《王者光荣》《阴阳师》 等手游,肯定留神到过它的启动动画、皮肤立绘卡片等场景,常常采纳动态底图加部分液态流动成果的简略动画,这些流动动画可能呈现在缓缓流动的水流 🌊、顶风飘动的旗号 🎏、游戏角色衣袖 🧜‍♀️、随着工夫缓动的云、雨、雾天气成果 等。这种过渡成果不仅节俭了开发全量动画的老本,而且使得游戏画面更加热血、冒险、奥德赛、高级,也更加容易吸引玩家氪金 💰

本文应用前端开发技术,联合 SVGCSS 来实现相似的液化流动成果。本文蕴含的知识点次要包含:mask-image 遮罩、feTurbulencefeDisplacementMap 滤镜、filter 属性、canvas 绘制办法、TimelineMax 动画以及input[type=file] 本地图片资源加载等。

成果

先来看看实现成果,上面几个示例以及 👆 文章 Banner 图都是利用了由本文内容生成的液态流动动画成果。因为GIF 图压缩比较严重,动画成果看起来不是很晦涩 🙃,大家无妨通过以下演示页面链接,亲自体验一下成果,生成本人的 传说典藏 皮肤立绘吧 😅

  • 👁‍🗨 在线体验:https://dragonir.github.io/pa…
  • 👁‍🗨 在线体验:https://codepen.io/dragonir/f…

🌀 雾气扩散 塞尔达传说:旷野之息

💃 衣袖飘动 貂蝉:猫影幻舞

🌅 湖光稳定

🔠 文字液化

📌 ps:体验页面部署在 Gitpage 上传图片性能不是真正上传到服务器,而是只会加载到浏览器本地,页面不会获取任何信息,大家能够释怀体验,不必放心隐衷透露问题。

实现

页面次要由 2 局部形成,顶部用于加载图片 ,并且能够通过按住 🖱 鼠标划动的形式绘制热点门路,给图片增加流动成果;底部是管制区域,点击按钮 🔘 革除画布,能够革除绘制的流动动画成果、点击按钮 🔘 切换图片能够加载本地的图片。

📌 留神,还有一个隐形的性能,当你绘制实现时,能够点击 🖱 鼠标右键,而后抉择保留图片,保留的这张图片就是咱们绘制流体动画门路的热点图,利用这张热点图,应用本文的 CSS 常识,就能把动态图片转化成动态图啦!

HTML 页面构造

#sketch 元素次要是用于绘制和加载流动成果热点图的画板;#button_container 是页面底部的按钮管制区域;svg 元素用于利用其 filter 滤镜实现液态流动动画成果,包含 feTurbulencefeDisplacementMap 滤镜。

<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>

💡 feTurbulence 和 feDisplacementMap

  • feTurbulence:滤镜利用 Perlin 噪声函数创立了一个图像,利用它能够实现人造纹理比如说云纹、大理石纹等模仿滤镜成果。
  • feDisplacementMap:映射置换滤镜,该滤镜用来自图像中从 in2 到空间的像素值置换图像从 in 到空间的像素值。即它能够扭转元素和图形的像素地位,通过遍历原图形的所有像素点,feDisplacementMap 从新映射替换一个新的地位,造成一个新的图形。该滤镜在业界的支流利用是对图形进行形变,扭曲,液化。

CSS 款式

接着看看款式的实现,main 元素作为主容器并将主图案作为背景图片;canvas 作为画布占据 100% 的空间地位;.mask.mask-inner 用于生成如下图所示热点门路与背景图相溶的成果,这种成果是借助 mask-image 实现的。最初,为了生成动静流动成果,.mask-inner 通过 filter: url(#heat) 将后面生成的 svg 作为滤镜起源,后续行将在 JavaScript 中通过不间断批改 svg 滤镜的属性,来生成液态流动动画。

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')
}

💡 mask-image

mask-image CSS 属性用于设置元素上遮罩层的图像。

语法

// 默认值,通明的彩色图像层,也就是没有遮罩层。
mask-image: none;
// <mask-source><mask>或CSS图像的url的值
mask-image: url(masks.svg#mask1);
// <image> 图片作为遮罩层
mask-image: linear-gradient(rgba(0, 0, 0, 1.0), transparent);
mask-image: image(url(mask.png), skyblue);
// 多个值
mask-image: image(url(mask.png), skyblue), linear-gradient(rgba(0, 0, 0, 1.0), transparent);
// 全局值
mask-image: inherit;
mask-image: initial;
mask-image: unset;

兼容性

此性能某些浏览器尚在开发中,须要应用浏览器前缀以兼容不同浏览器。

JavaScript 办法

① 绘制热点图

监听鼠标挪动和点击事件,在 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});
    `;
  });
};

绘制实现后,能够在页面中右键保留生成的稳定门路热点图,间接将绘制称心的热点图放到 CSS 中,就能给喜爱的图片增加部分稳定成果了,上面这张图片就是本示例页面应用的稳定的热点门路图。

② 生成动画

为了生成实时更新的稳定成果,本文应用了 TweenMax 来通过扭转 feTurbulencebaseFrequency 属性值来实现,应用其余动画库或应用 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);

③ 革除画布

点击革除画布按钮,能够清空曾经绘制的稳定门路,次要是通过革除页面元素 mask-image 的属性值以及清 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();
  })
});

④ 切换图片

点击切换图片,能够加载本地的一张图片作为绘制底图,该性能是通过 input[type=file] 来实现图片资源的获取,而后通过批改 CSS 将它设置成新的画布背景。

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;
  `;
};

到这里,全副性能都实现结束了,大家赶快制作一张本人喜爱的 史诗皮肤奥德赛小游戏 的启动页面吧 🤣

📥 源码地址:https://github.com/dragonir/paint-heat-map

总结

本文蕴含的新知识点次要包含:

  • mask-image 遮罩元素
  • feTurbulencefeDisplacementMap svg滤镜
  • filter 属性
  • Canvas 绘制办法
  • TimelineMax 动画
  • input[type=file] 本地图片资源加载

想理解其余前端常识或其余未在本文中详细描述的 Web 3D 开发技术相干常识,可浏览我往期的文章。转载请注明原文地址和作者。如果感觉文章对你有帮忙,不要忘了一键三连哦 👍

附录

  • 我的3D专栏能够点击此链接拜访 👈
  • [1]. 🌐 应用Three.js实现炫酷的赛博朋克格调3D数字地球大屏
  • [2]. 🦊 Three.js 实现3D凋谢世界小游戏:阿狸的多元宇宙
  • [3]. 🔥 Three.js 火焰成果实现艾尔登法环动静logo
  • [4]. 🐼 Three.js 实现2022冬奥主题3D趣味页面,含冰墩墩
  • ...
  • [1]. 📷 前端实现很哇塞的浏览器端扫码性能
  • [2]. 🌏 前端瓦片地图加载之塞尔达传说旷野之息
  • [3]. 😱 仅用CSS几步实现赛博朋克2077格调视觉效果
  • ...

参考

  • [1]. https://developer.mozilla.org/zh-CN/docs/Web/SVG/Element/feTurbulence
  • [2]. https://developer.mozilla.org/zh-CN/docs/Web/SVG/Element/feDisplacementMap
  • [3]. https://developer.mozilla.org/zh-CN/docs/Web/CSS/mask-image
  • [4]. https://developer.mozilla.org/zh-CN/docs/Web/CSS/filter

作者:dragonir 本文地址:https://www.cnblogs.com/drago…

评论

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

这个站点使用 Akismet 来减少垃圾评论。了解你的评论数据如何被处理