行歌

行歌 查看完整档案

上海编辑复旦大学  |  计算机 编辑上海码语广告科技有限公司  |  技术总监 编辑 dev.xingway.com/blog 编辑
编辑

微信公众号码语派-发布前端原创教程,关注最新技术。

个人动态

行歌 发布了文章 · 2020-09-09

炫酷!用Perlin Noise生成随机地形

好久不发THREE.js的教程了,今天给大家带了一个小案例,介绍如何用柏林噪声在THREE.js中生成随机地形,先来看下效果。

ani

随机数和噪声


噪声实际上就是随机数生成器,普通噪声生成的随机数毫无规律可言。

在上世纪80年代,柏林噪声的发明者Ken Perlin尝试用普通噪声模拟随机效果,用于正在制作的经典迪士尼电影电子世界争霸战(TRON),但一直无法得到满意的效果,柏林噪声算法就这样应运而生。

2010年TRON重拍版本

柏林噪声

自然界中层叠的山峦,大理石的纹理,海面高低起伏的波浪,这些现象看似杂乱无章,却都有内在规律可循,普通的噪声无法模拟出这些自然效果,柏林噪声算法使这些成为可能。

用柏林噪声算法可以得到一组平滑插值的随机数,它们之间相互关联,可以用来生成接近自然的随机值。下图中,左边是普通噪声生成的随机纹理,右边是柏林噪声生成的纹理,可以看到后者生成的纹理更加自然平滑,随机值之间有明显过渡效果。

t1

我用canvas绘制了两组图像,分别是用两种算法生成一些随机点,然后将这些点连接起来,可以看到右侧柏林噪声得到的是一条相对平滑的曲线,而左边普通噪声的图形毫无规律可言。

普通噪声和柏林噪声随机点连线

生成随机地形

在THREE.js我们用一个平面来生成随机地形,平面是由一系列顶点构成的,我们只需要改变这些顶点的z轴坐标值,就能产生高低起伏的效果,我们先用随机噪声来试试。

let pos = this.groundGeo.attributes.position.array
for(let i = 0; i < pos.length; i+=3) {
  pos[i+2] = Math.random() * 2
}
this.groundGeo.attributes.position.needsUpdate = true

随机噪声产生的地形

可以看到随机噪声产生的地形太过于生硬,肯定不是我们想要的结果,再来试试柏林噪声。

let pos = this.groundGeo.attributes.position.array
for(let i = 0; i < pos.length; i+=3) {
  pos[i+2] = this.simplex.noise2D(tx, ty) * 2
}
this.groundGeo.attributes.position.needsUpdate = true

我们得到了更加自然平滑的地形效果,这里我们使用的Simplex Noise是柏林噪声的更高效,简介的实现,也是柏林噪声的作者提出的,这里我们不关心这些算法的实现细节。

柏林噪声产生的地形效果

移动地形

要实现地形向观察者移动,我们只需要改变柏林噪声的x坐标,让它随着时间递增。

let pos = this.groundGeo.attributes.position.array
this.offsetX += 0.01
for(let i = 0; i < pos.length; i+=3) {
  let tx = this.offsetX
  let ty = this.offsetY
  tx += row * 0.05
  ty += col * 0.05
  pos[i+2] = this.simplex.noise2D(tx, ty) * 5
}
this.groundGeo.attributes.position.needsUpdate = true

这里使用一个offsetX变量,它的值在每帧循环中递增。注意,我只贴了关键代码,具体细节可以点击阅读原文来获得本案例的源文件。

通过调整各项参数,我们还可以模拟出更多效果,例如:棱角分明的山峰,高低起伏的海浪,或者外形更加平滑的沙丘等等。

总结

Perlin Noise柏林噪声在游戏和图形领域被广泛的应用,利用它可以模拟出许多有趣的视觉效果,你学到了嘛!

原文地址:https://dev.xingway.com/threejs-perline-noise/

关注

查看原文

赞 1 收藏 1 评论 0

行歌 发布了文章 · 2020-07-24

公众号SVG序列帧动画

最近在水果公众号上看到一段菊花绽放的动画,丝般润滑的感受,吹弹可破的花瓣,让我忍不住看了下究竟,将所得整理了下,作为小技巧分享给大家。

具体看下图,视频转换成GIF质量下降,有兴趣可以去水果公号看。

水果绽放动画

瞄了下源代码,居然是用SVG动画拼成的序列帧。序列帧是什么?水果前端为什么好好的GIF图不用,要用序列帧?原因听我慢慢道来。

什么是序列帧?

序列帧就是一系列静止图像,将这些图像按照一定的频率播放,就形成了连续的动画,一般认为到达到每秒24帧的速率,人们才会看到平滑动画,大家平时看到的视频和动图本质都是由序列帧组成的。

电影画面中序列帧

翻页动画

使用序列帧有什么好处?

  1. 可以保证图片质量 因为公众号后台会压缩上传的GIF图片,而且GIF图像本身对透明度支持不佳,对图像质量要求较高的场景就不合适了。而使用序列帧,我们可以使用无损的PNG图片,保证动画高清显示。
  2. 动画更平滑 相对于GIF动画,程序控制的动画用户体验更加平滑流畅。

怎样使用序列帧?

在平常的web开发中,我们可以很容易地用js或者css实现类似的帧动画,由于公众号图文环境的限制,我们只能用SVG来实现这个效果,水果公号里用animate标签巧妙的解决这个问题。

通过把每一帧的图片单独放在一层里,并使每一层重叠在一起。使用透明度动画来控制每个图片帧的出现时间,产生连续播放的动画效果。

绽放动画的序列帧叠加演示

<div style="height:0;">
  <svg opacity="0" viewBox="0 0 828 828" style="width:100%;background-image:url('img/02.png');background-size:100% auto;background-repeat:none;transform:rotateZ(0deg);">
    <animate attributename="opacity" begin="1.3125s" dur="6.25s" values="1; 1; 0; 0;" keytimes="0; 0.010; 0.012; 1" fill="freeze"></animate>
  </svg>
</div>

<div style="height:0;">
  <svg opacity="0" viewBox="0 0 828 828" style="width:100%;background-image:url('img/01.png');background-size:100% auto;background-repeat:none;transform:rotateZ(0deg);">
    <animate attributename="opacity" begin="1.25s" dur="6.25s" values="1; 1; 0; 0;" keytimes="0; 0.010; 0.012; 1" fill="freeze"></animate>
  </svg>
</div>

原理懂了,可是动画整整有70多帧,复制黏贴显然不适合我这样(lan)机智的前端,于是拿出了看家本领javascript大法。

const generateFrames = () => {
  const container = document.querySelector('#container')
  const totalFrames = 72
  let html = ''
  for(let i = totalFrames; i > 0; i--) {
    const inter = 0.0625
    const height = i == 1 ? 'auto' : '0px'
    const opacity = i == 1 ? 1 : 0;
    const begin = (1.5 + inter * i) + 's'
    
    const dom = `
      <div token interpolation" >${height};">
        <svg opacity="${opacity}" viewBox="0 0 828 828" token interpolation" >${i}.png');background-size:100% auto;background-repeat:none;transform:rotateZ(0deg);">
          <animate attributename="opacity" begin="${begin}" dur="6.25s" values="1; 1; 0; 0;" keytimes="0; 0.010; 0.012; 1" fill="freeze"></animate>
        </svg>
      </div>
    `
    html += dom
  }
  container.innerHTML = html
}
generateFrames()

将生成的代码,在开发者工具中复制黏贴。

chrome开发者工具中复制黏贴

搞定,收工!

源码:
https://dev.xingway.com/tutor...

原文地址:
https://dev.xingway.com/svg-f...

查看原文

赞 0 收藏 0 评论 0

行歌 分享了头条 · 2020-07-15

基于three.js不错的3d特效

赞 0 收藏 1 评论 0

行歌 发布了文章 · 2020-07-15

Three.js无限3D时空穿梭特效

可能每个人的童年都有个时空穿梭梦,印象深刻的是第一次看《机器猫》,主角康夫卧室的抽屉就是时空隧道的入口,跳进隧道的那一刻,时间浮光掠影般从身边流过,仿佛进入了异世界。

这次我们用three.js实现一个3d隧道穿梭效果,打开异世界的大门。 它也可以是一个跑酷游戏原型,又或者是3D空间导航,具体怎么应用由你来决定,先来看下效果。

无限循环时空隧道

无限循环的隧道实际上是一个首尾无缝相连的管道,你可以将它的形状想象成一个甜甜圈。我们只要将摄像机放置在这个管道内部,并沿着管道路径运动就能实现这种穿越效果。

实现无限循环隧道

首先要实现这样一个管道我们可以借助three.js中TubeGeometry对象,TubeGeometry能够沿着一条3D曲线创建管道。我们要做的是先创建一条曲线,曲线是用坐标函数建立的,我们先拿TubeGeometry的官方示例试一下。

CustomSinCurve.prototype.getPoint = function ( t ) {
    var tx = t * 3 - 1.5
    var ty = Math.sin( 2 * Math.PI * t )
    var tz = 0
    return new THREE.Vector3( tx, ty, tz ).multiplyScalar( this.scale )
}

官方示例的getPoint函数绘制了一个烟斗状的曲线管道,看起来平淡无奇,别着急我们来逐步完善。

用官方示例绘制出的漏斗状曲线

改变下getPonit函数中的公式,我们马上得到了一个封闭的圆环管道。

CustomSinCurve.prototype.getPoint = function (t) {
  const tx = Math.cos(2 * Math.PI * t)
  const ty = Math.sin(2 * Math.PI * t)
  const tz = 0
  return new THREE.Vector3(tx, ty, tz).multiplyScalar(this.scale)
}

封闭圆环

为隧道添加材质

贴图是这个效果的灵魂,准备一张1024×1024尺寸的材质贴图,它会重复地显示在管道模型表面,注意贴图中明暗交替的线条,这是实现背景光线效果的关键。

隧道的材质贴图

设置材质的贴图方式和重复次数,使材质沿曲线方向重复延展,我们得到了一个贴好图的隧道模型。

material.map.wrapS = THREE.RepeatWrapping
material.map.wrapT= THREE.RepeatWrapping
material.map.repeat.set(10, 1)

贴图后的隧道

设置摄像机路径

要实现穿越效果,我们要将摄像机放在模型内部,并沿着隧道路径运动。官方的案例中也有一个相机沿曲线运动的例子,但经过试验效果并不理想,所以这里使用另外一种方法,这种方法考虑了隧道路径的法向量,使转弯处过渡效果更加自然。由于代码较长,就不贴在这里了,具体可以参考本篇教程的源文件。

沿路径添加物体

为了在隧道路径上添加物体,先要在管道路径上通过getPointAt方法上获得坐标点,然后将物体设置在坐标点处,这里的物体是一个个透明贴图的平面对象。

for(let i = 0; i < 20; i++) {
  const plane = mesh.clone()
  const pos = this.tubeGeometry.parameters.path.getPointAt(i * 0.04)
  plane.position.copy(pos)
  this.scene.add(plane)
}

最后,将这些平面对象旋转朝向摄像机,可以通过设置四元数属性和相机相同轻松搞定。四元数是3D空间旋转常用的数学方法,可以避免使用矩阵旋转时产生的万向锁问题,由于它数学原理比较复杂,这里就不做介绍了,有兴趣可以找相关资料学习。

this.planes.forEach(plane => {
  plane.quaternion.copy(this.camera.quaternion)
})

总结

我们已经掌握了如何使相机沿着自定义曲线运动,并实现了一个炫酷的空间隧道效果。接下来请发挥你的想象,做出更有趣的应用。

这里是一些建议:可以用three.js的postprocessing加入后期效果,使它更有科技感;可以加入鼠标交互,使摄像机根据鼠标滚轮向前移动;可以和cannon.js物理库结合,加入碰撞检测等等。

教程源文件:

https://github.com/imokya/threejs-tunnel-effect

原文链接:
https://dev.xingway.com/threejs-infinite-tunnel/

关注

查看原文

赞 1 收藏 1 评论 0

行歌 发布了文章 · 2020-07-12

避开喧嚣的三亚,带你看不一样的海南

生活中不能只有代码,也要去看看世界,最近又去了趟海南,就把这次的见闻作为一篇旅行攻略带给需要的人。

提到海南,人们首先想到的是三亚,那里有开阔干净的沙滩,清澈的海水,傍晚清凉的海风。而满怀期待来到这里的游客往往会大失所望,等待他们的是拥挤脏乱的海滩,傍晚被喧嚣的秧歌队和广场舞占领的休闲区,在家门口就能吃到的所谓美食,而价格却高了数倍。

几年前摄于三亚

被炒房团和候鸟族占领的三亚,物价飞涨,而当地工资收入却远没跟上物价上涨速度,本地人选择逃离,三亚已经毫无本地特色。

这次我避开喧嚣的三亚,第一站先去位于海南岛西北部的儋州,看一下原汁原味的海南生活。交通路线:从海口美兰机场做环岛高铁,经1个多小时到达儋州白马井站。

打车6公里左右,来到儋州中和古镇, 这里比较有名的是东坡书院,北宋文学家苏东坡曾在这里谪居三年,讲学明道, 对之后儋州的教育文化产生了深远影响。

走在空气清新的小镇上,胡同里碰到赶牛的老伯

路边随处可见的热带水果,好吃又便宜,干净又卫生

空气好不好,看照片的通透度

自家的走地鸡,晚上就翻你们的牌了

晚上吃顿不含激素和添加剂本地菜,雷霆嘎巴

饭后吃盘自家种的水果,在树荫下享受明月清风的安逸。

清风朗月不用一钱买

树荫下的蜂窝

7月初的儋州,每天午后都会下一场雨,突然有种在江南的感觉。

在儋州的悠闲日子转眼就过去了,下一站是海口,虽然是海南的省会城市,但不会有三亚的喧闹,也很好的保留了本地特色。

先去了海口日月光免税店,6月份海南自由贸易港方案确定后,每人有10万元的免税额度,凭返程机票就可以购买免税商品,有需要的可以来逛逛。

海口日月光免税店

海南夏天随处可见的水果-黄皮,果肉和口感像葡萄,酸酸甜甜的,如果用来榨汁,再放上一些盐,就是一杯很好的消暑解渴的饮料。

黄皮用来榨汁也不错

红色带刺的是红毛丹,又叫毛荔枝,价格比荔枝略贵,没有荔枝的甜腻,很爽口。

红毛丹

这菠萝蜜和老板娘差不多高了

来海口怎能不吃小吃,海口比较有名的是骑楼小吃街,但是1月份新冠疫情以后就没有再开放,不要有什么遗憾,骑楼小吃虽然有名,但本地人基本不去,里面的小吃也很一般,去的大都是慕名而来的外地人。

这里推荐大家去海南大学南门的小吃一条街,位于海口北部的海甸岛上。每天下午5点左右小吃街就陆续出摊了,一直到深夜都是灯火通明,人流络绎不绝。小吃街里除了有许多海南本地经典美食,如:清补凉,陵水酸粉,炸炸等, 也扎堆了全国各地的特色小吃。在大学小吃街里逛逛仿佛回到了学生时代,据说很多毕业的同学依然对这里念念不忘。

海南大学南门小吃街

这里可以吃到价格实惠的水果,鲜果榨汁也只要6-8元1杯。

价格实惠的水果

吃到天黑还意犹未尽

酒足饭饱后,推荐你来海口湾看看日落,吹吹海风,欣赏海口迷人夜景。

海口湾世纪大桥

海口湾夜景

夜晚海口湾出来锻炼和散步的人们,享受着海风和远处的夜景,海口人是幸福的。

查看原文

赞 0 收藏 0 评论 0

行歌 发布了文章 · 2020-07-05

基于WEBGL的文字飞溅特效

基于WEBGL的文字飞溅特效

今年的梅雨很敬业,上海已经下了一个月的雨,仍然没有要停下来的意思,前一秒还是大太阳,下一秒被浇成落汤鸡,大家管这叫暴力梅。

蹭下暴力梅的热度,今天给大家带来一阵文字雨,先来看下效果。

灵感来源于LeoSans中的一个示例,LeoSans是一个开源的文字动画库,
作者是Google的创意开发者,有兴趣可以在github上搜下。

文字撞击水面后飞溅的效果被业界称为Gooey Effect,翻译过来就是粘液特效,是前端小伙伴用来模拟液体效果的利器,也催生出了很多玩法。

用来做UI交互特效

用来做大屏互动特效

看起来花里胡哨,实现起来只需要两个简单的步骤:

  1. 对效果区域添加模糊滤镜
  2. 在模糊效果的基础上增加图像对比度

是不是很简单?而且用css或者是svg,可以轻松的实现这样的效果,而我们今天介绍的是第三种方案,也是运行效率最高的一种,用webgl的shader来实现。

这次我们使用pixi.js动画库,相信很多前端小伙伴对这个老牌动画库已经很熟悉了,它的最新版本已经默认采用webgl渲染模式,在现代浏览器上有较高性能提升。

下面介绍程序的几个要点,具体细节可以参考文末的源代码。

创建应用

先来创建一个pixijs应用,并将画布设置充满整个屏幕。

initScene() {
  this.app = new PIXI.Application({
    width: window.innerWidth,
    height: window.innerHeight,
    backgrondColor: 0xffffff
  })
  this.app.view.style.width = '100%'
  this.app.view.style.height = '100%'
  this.app.view.style.position = 'absolute'
  this.el.appendChild(this.app.view)
}

添加滤镜

刚才说过,要实现Gooey效果有两个步骤。我们先对整个画布应用模糊滤镜,然后在应用一个自定义滤镜来增加对比度,pixijs中的滤镜都是对webgl shader的封装。

addFilter() {
  const blurFilter = new PIXI.filters.BlurFilter(3) //模糊滤镜
  //自定义片段着色器,增加alpha区域对比度
  const fs = `
    varying vec2 vTextureCoord;
    uniform sampler2D uSampler;
    void main(void) {
      vec4 color = texture2D(uSampler, vTextureCoord);
      if(color.a > 0.6) {
        gl_FragColor = vec4(1.0, 1.0, 1.0, 1.0);
      } else {
        gl_FragColor = vec4(0.0, 0.0, 0.0, 1.0);
      }
    }
  `
  const thresholdFilter = new PIXI.Filter(undefined, fs, {}) 
  this.app.stage.filters = [blurFilter, thresholdFilter]
  this.app.stage.filterArea = this.app.renderer.screen
}

文字效果

要实现文字撞击水面的飞溅效果,首先要取得汉字的字形路径,然后用一些粒子填充路径,最后对粒子做简单的物理运动,实现触底碰撞效果。

每个汉字都是背景透明的png图片,我们只要通过canvas API对这些图片进行像素采样,就能获得每个文字的点阵坐标。

getImageData() {
  const canvas = document.createElement('canvas')
  const ctx = canvas.getContext('2d')
  img.onload = () => {
    ctx.drawImage(img, 0, 0, size, size)
    const imgData = ctx.getImageData(0, 0, size, size)
    for(let i = 0; i < imgData.data.length; i+=4) {
      const alpha = imgData.data[i+3]
      const index = i / 4
      if(alpha > 0) {
        this.data.push({
          x: index % size,
          y: index / size | 0
        })
      }
    }
    this.createChar()
  }
  img.src = src
}

粒子弹性碰撞

每个粒子垂直方向有一个重力加速度,每次触底碰撞的能量损失,使反弹高度降低。

update() {
  this.vy += this.gravity
  this.x += this.vx
  this.y += this.vy
  if(this.y > this.bottom) {
    this.vy *= this.bounce 
    this.hit = true
  } 
}

总结

现在你已经掌握粘性特效的原理,发挥你的想象力尝试做出一些有趣的效果吧!欢迎在留言区分享你的成果。

github源码:
https://github.com/imokya/word-splash

原文链接:
https://dev.xingway.com/character-splash/

查看原文

赞 2 收藏 2 评论 0

行歌 发布了文章 · 2020-06-04

玩转AR图形识别小程序

玩转AR图形识别小程序

这些年深度学习炙手可热,人脸识别,图像分类,目标检测等技术已经应用到我们生活的方方面面。作为一个时刻保持好奇心的程序猿想入门深度学习,但又苦于门槛较高,难以获得理想的学习成果。好在一些大厂已经推出了深度学习云平台,让我们前端小白也能体验深度学习的乐趣。

这次我们基于百度AI开放平台EasyDL来实现一个logo图像识别小程序,半天时间轻松搞定客户BABA的需求。

logo目标检测演示

1: 创建训练模型

要实现logo识别的功能,我们先要创建一个物体检测模型。

访问百度EasyDL地址 https://ai.baidu.com/easydl 在操作平台菜单中,选择物体检测选项。

1.1 选择创建模型

1.2 为模型命名,模型归属选择个人。

2: 创建训练数据集

模型创建好以后,我们要为模型提供训练数据,所谓训练数据就是我们要识别的logo图片。

模型就像一个懵懂的孩子,我们要用训练数据教他识别图片中的logo,让他能举一反三,以后遇到不在数据集中的图片也能准确的识别出来。

2.1 准备训练数据

为了提高识别精度,我们需要用手机拍摄不同的光线,角度,背景下的logo图片上传,图片数据越多,越有助于提高模型的识别精度,一般建议上传40张左右。

2.2 标注数据

上传后,用鼠标拖拽矩形区域标注出logo图形位置,并设置统一的标签名,然后点击左下角的保存按钮。如果嫌手动标注麻烦,也可以尝试右下角的智能标注

2.3 开始训练

训练时间和数据集大小成正比,以我30多张的训练数据为例,大概需要20分钟左右,已经很快啦,如果是在本地单机上训练可能要几小时的时间,这就是云服务分布计算的好处。

2.4  校验模型

训练结束后,我们需要对模型进行校验,看看模型的识别精度是否符合预期。

上传几张不在数据集中的照片来检验结果,如果效果满意,我们就可以进行下一步,发布模型。

3: 发布模型

3.1  提交申请

发布模型前需要提交审核,填写服务名称和接口地址,提交申请。

审核成功后,在操作列表中点击服务详情->立即使用。

3.2  创建应用

在使用接口前,我们还需要创建一个应用,应用可以理解为一个容器,一个应用可以包含多个接口,为这些接口提供访问键值,秘钥等参数。

应用创建结束后,会看到分配的API Key和Secret Key,这些都是接口调用需要携带的参数。

4: 在小程序中调用识别接口

4.1 接口token验证

在调用识别接口前先要拿到access_token令牌,这是所有接口调用的前提条件。

wx.request({
  url: API_AUTH_URL, //token授权地址
  method: 'GET',
  data: {
    grant_type: 'client_credentials',
    client_id: CLIENT_ID, //API Key
    client_secret: CLIENT_SECRET //Secret Key
  },
  success: (res) => {
    _access_token = res.data.access_token
    this._getImageData()
  }
})

4.2 调用接口

传递access_token和要识别的图片等参数,具体可以参考官方的技术文档。

wx.request({
  url: `${API_URL}?access_token=${_access_token}`, //access_token作为query参数
  method: 'POST',
  data: {
    image, //摄像头帧图片用base64编码
    threshold: 0.1
  }
})

5:总结

百度EasyDL平台, 让我们无需机器学习专业知识,只用半天时间就开发出一个深度学习的小应用。相信今后这样的平台也会越来越多,提供给开发者更多的玩法解决工作中的实际问题。

附上小程序github源码地址: https://github.com/imokya/object-detect

查看原文

赞 0 收藏 0 评论 0

行歌 发布了文章 · 2020-05-30

基于three.js实现一个粒子系统

在此系列教程的前两部分,我们了解了实现一个粒子系统需要具备的基础知识。这次我们会利用这些知识来实现一个简单的粒子系统,并把这个系统应用到自己的项目中去。

粒子系统演示之星轨

为实现粒子系统,我们已经不需要什么新知识,我们需要做的是在粒子中添加更多的物理参数,并合理地组织代码来实现系统的复用性,需要添加的物理参数可以参考文尾的源代码,我们这里着重介绍下代码的组织。

为了实现粒子系统ParticleSystem的复用,我引入了发射器Emitter的概念,每一种粒子特效诸如:雪花,火焰,爆炸等被实现为发射器的一个子类。粒子系统扮演管家的角色控制发射器的开始,停止和销毁等操作。

粒子系统类结构

每一种发射器按照形状可分为立方体和球体两大类。不难想象诸如雪花,星空,萤光等场景更适合用立方体空间粒子实现,而火焰,烟雾等特效的形状更接近于球体,可以参考下图的对比。

粒子系统之雪花

粒子系统之火焰

粒子系统之萤光

要注意的是粒子材质的混合模式要根据粒子类型来设置,诸如火焰,荧光的粒子,需要将混合类型设置为 THREE.AdditiveBlending模式,Additive混合模式的原理是将着色器中的两个像素叠加,这样会产生更强的光晕效果。

粒子系统的主要内容就是这些了,除了上面的演示效果外,理论上可以调整参数实现更多炫酷效果,有待你去发现,如果有疑问可以参考源码或给我留言。

最初有实现一个粒子系统的念头是因为自己正在做的three.js小游戏,游戏中需要加入诸如爆炸,火焰喷射等粒子特效,而three.js中并没有内置这样的工具。于是决定学习自己实现一个,在学习了官方的实例和github的源码后,我尝试把学到知识分享出来,于是就有了这个系列教程,如果你也能从中学到点什么,我觉得一切都是值得的,祝学习愉快!

DEMO演示

Github源码: https://github.com/imokya/Par...

查看原文

赞 1 收藏 0 评论 0

行歌 发布了文章 · 2020-05-30

公众号SVG动画交互实战

越来越多的公众号在图文消息中加入了SVG动画交互效果,SVG支持事件触发动画,相对于单独做一个H5而言,由于公众号消息依托于微信服务器,为广告主节约了服务器流量成本。这次我们以苹果公众号的一篇交互消息为例,剖析下里面的效果是怎样实现的。

苹果公众号SVG交互

动画主要分为两屏,第一屏出现闪动文字提示用户点击交互,用户点击后第一屏动画消失接着播放第二屏的gif动画,最后画面停止在产品图片帧上。

1.首先构建第一屏动画:

1.1 SVG的基本结构

<svg viewBox="0 0 1080 620" width="100%" height="620px" preserveAspectRatio="xMinYMin meet" xmlns="http://www.w3.org/2000/svg"></svg>

先来介绍viewBox属性

viewBox=”x, y, w, h”

x、y 控制SVG内所有元素的相对位置。w,h用来控制svg宽高,这里的宽高并不是svg元素的dom尺寸,而是svg的内分辨率,受svg的width,height和preserveAspectRatio等属性值影响。

preserveAspectRatio=”xMinYMin meet”

preserveAspectRatio属性用来设置viewBox的缩放和对齐方式,xMinYMin meet的意思是,根据视口的宽高进行等比例缩放,这里的视口就是指width和height值组成的矩形区域。

1.2 加入闪动文字

<svg viewBox="0 0 1080 620" width="100%" height="620px" preserveAspectRatio="xMinYMin meet" xmlns="http://www.w3.org/2000/svg">
  <text x="340" y="1750" fill="#fff" >>点一下屏幕,有请主角<</text>
</svg>

设置文字的位置和颜色属性。

1.3 为文字添加动画,这里需要用到 <g>和<animate>标签

<svg viewBox="0 0 1080 620" width="100%" height="620px" preserveAspectRatio="xMinYMin meet" xmlns="http://www.w3.org/2000/svg">
  <g>
     <animate attributeName="opacity" begin="0s" dur="1s" values="1;0;1" repeatCount="indefinite"></animate>
     <text x="340" y="1750" fill="#fff" >>点一下屏幕,有请主角<</text>
  </g>
</svg>
  • animate标签用来对元素的某个属性进行动画。
  • attributeName指定属性名,这里是透明度opacity。
  • begin指定动画开始的时间,可以是一组用分号分隔的值。
  • dur指定动画的时长,值越小动画越快,反之亦然。
  • values指定attributeName属性的变化值,可以是单值也可以是分号分隔的列表。这里的1;0;1指定是透明度在0->1->0之间变换,产生闪烁的效果。
  • g标签即group的缩写,用来对元素进行组合, 这样animate效果就限制在组内。

1.4 加入首屏和动画图片

<svg  version="1.1" viewBox="0 0 1080 620" preserveAspectRatio="xMinYMin meet" xmlns="http://www.w3.org/2000/svg">
  <g>
    <g>
      <foreignObject x="0" y="0" width="1080" height="1950">
        <svg ></svg>
      </foreignObject>
      <foreignObject x="0" y="0" width="1080" height="1950" transform="translate(1080, 0)">
        <svg xmlns="http://www.w3.org/2000/svg" >
        </svg>
      </foreignObject>
      <g>
        <animate attributeName="opacity" begin="0s" dur="1s" values="1;0;1" repeatCount="indefinite"></animate>
        <text x="340" y="1750" fill="#fff" >>点一下屏幕,有请主角<</text>
      </g>
    </g>
  </g>
</svg>
  • 这里我们加入了两组foreignObject对象用来显示封面和gif动画图片。
  • foreignObject可以理解成一个svg容器,支持x, y, width, height, transform位移等属性,这里我们将包含gif动画图片的foreignObject位移属性设置为 transform=”translate(1080, 0),使gif动画沿x轴向右移动自身宽度距离,使动画开始隐藏起来,等待事件触发显示。
  • 我们将元素放在不同的分组里,方便后面添加事件。

1.5 加入事件触发

<svg  version="1.1" viewBox="0 0 1080 620" preserveAspectRatio="xMinYMin meet" xmlns="http://www.w3.org/2000/svg">
  <animate attributeName="height" begin="click+3.5s" restart="never" dur="0.01s" from="620" to="0" fill="freeze"></animate>
  <animate attributeName="opacity" begin="click+3.5s" restart="never" dur="0.02s" from="1" to="0" fill="freeze"></animate>
  <g>
    <animateTransform attributeName="transform" type="translate" fill="freeze" calcMode="discrete" restart="never" keyTimes="0;0.000001;1" values="0 0;-1080 0;-1080 0" dur="1200s" begin="click"></animateTransform>
    <g>
      <foreignObject x="0" y="0" width="1080" height="1950">
        <svg ></svg>
      </foreignObject>
      <foreignObject x="0" y="0" width="1080" height="1950" transform="translate(1080, 0)">
        <svg xmlns="http://www.w3.org/2000/svg" >
        </svg>
      </foreignObject>
      <g>
        <animate attributeName="opacity" begin="0s" dur="1s" values="1;0;1" repeatCount="indefinite"></animate>
        <text x="340" y="1750" fill="#fff" >>点一下屏幕,有请主角<</text>
      </g>
    </g>
  </g>
</svg>
  • animateTransform用来对元素进行位移,旋转,斜切等操作。
  • transform,可以是 translate、scale、rotate、skewX、skewY 。
  • fill,指定动画间隙的填充方式。支持参数有:freeze、remove。remove是默认值,表示动画结束直接回到开始的地方。freeze表示动画维持结束后的状态。
  • restart, 支持的参数有always、whenNotActive、never。always是默认值,表示每点一次重新执行动画;whenNotActive表示动画正在进行的时候不能重启动画;never表示动画仅执行一次。
  • begin, 延迟时间已经介绍过,这里补充下click,表示点击后立即触发, click+2表示点击后2秒触发。
 <animateTransform attributeName="transform" type="translate" fill="freeze" calcMode="discrete" restart="never" keyTimes="0;0.000001;1" values="0 0;-1080 0;-1080 0" dur="1200s" begin="click"></animateTransform>

这段代码的意思是,鼠标点击后,我们将gif动画所在组向左移动1080距离,使原来隐藏的动画暴露出来,还记得我们开始是怎么隐藏gif动画的么。

<animate attributeName="height" begin="click+3.5s" restart="never" dur="0.01s" from="620" to="0" fill="freeze"></animate>
  <animate attributeName="opacity" begin="click+3.5s" restart="never" dur="0.02s" from="1" to="0" fill="freeze"></animate>

这里添加了两组animate动画,我希望在动画播放结束后隐藏当前的svg层,这里设置在点击事件3.5秒后触发,正好是动画播放的时间。

2.构建第二屏动画:

第二屏动画是一张静态图片,用来显示gif动画最后一帧的产品图,没有什么新知识点,如有疑问可以参考本文的源码。

最后介绍一下两屏动画的组织方式,我将每屏动画放在一个单独的div标签中,并设置div标签的高度为0,由于svg元素的高度不受父容器高度影响,结果会产生类似层叠定位效果,就像设置父容器定位position:absolute一样。

<div >
  <svg></svg>
</div>
<div >
  <svg></svg>
</div>

这样我们就可以将多组动画放在各自的div中,当使用animate动画隐藏了该组svg元素后,下面一层的svg就会展示出来等待交互。

3. 插入到公众号文章中

我们现在用到的图片都在本地,首先需要将用到的图片上传到公众号素材库中,提取url地址并替换掉本地图片地址。

因为公众号图文编辑器本身并不支持代码编辑,我们需要借助chrome开发者工具,将代码插入到编辑器并保存。

大功告成,快看看效果吧!

源码和效果演示:
https://dev.xingway.com/experiments/wechat/svg/

查看原文

赞 0 收藏 0 评论 0

行歌 关注了专栏 · 2020-05-29

码农田小齐

一起玩转算法和数据结构呀~ 公众号:码农田小齐

关注 2286

行歌 关注了用户 · 2020-05-29

手撕红黑树 @rip_red_black_tree

关注 489

行歌 关注了用户 · 2020-05-29

PerfMa @perfma

专业 Java 应用性能社区 https://club.perfma.com/
欢迎关注微信公众号:PerfMa 社区

关注 661

行歌 关注了专栏 · 2020-05-29

好未来技术团队

好未来(TAL)是一个以智慧教育和开放平台为主体,以素质教育和课外辅导为载体,在全球范围内服务公办教育,助力民办教育,探索未来教育新模式的科技教育公司。 好未来作为一家科技驱动的教育企业,始终坚持“用科技推动教育进步”的使命。一直以来,好未来技术团队致力于教育科技技术的研究与创新。

关注 1082

行歌 关注了专栏 · 2020-05-29

前端 1943

前端技术文章

关注 2466

行歌 关注了专栏 · 2020-05-29

TalkingData

TalkingData 开发同学们的经验与心得,欢迎一同探讨!

关注 3765

行歌 关注了专栏 · 2020-05-29

felix021

这个人很懒,什么都没留下。

关注 858

行歌 关注了用户 · 2020-05-29

MarkerHub @markerhub

公众号:MarkerHub
网站:https://markerhub.com
梳理Java知识,解析开源项目!

关注 167

行歌 关注了专栏 · 2020-05-29

Tomorrow

明天,你好

关注 319

行歌 关注了专栏 · 2020-05-29

SegmentFault 行业快讯

第一时间为开发者提供行业相关的实时热点资讯

关注 58746

行歌 关注了专栏 · 2020-05-29

SegmentFault 思否观察

SegmentFault 思否对开发者行业的洞见、观察与报道

关注 27825