前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >「Web Animation API 专题」用原生JS制作一个图片随机移动的动画

「Web Animation API 专题」用原生JS制作一个图片随机移动的动画

作者头像
前端达人
发布2019-08-02 17:11:36
3.9K0
发布2019-08-02 17:11:36
举报
文章被收录于专栏:前端达人前端达人
Web Animation API 介绍

当我们谈及网页动画时,自然联想到的是 CSS3 动画、JS 动画、SVG 动画 等技术以及 jQuery.animate() 等动画封装库,根据实际动画内容设计去选择不同的实现方式,然而,每个现行的动画技术都存在一定的缺点,如 CSS3动画必须通过JS去获取动态改变的值,一个动画效果分散在css文件和js文件里不好维护,setInterval 的时间往往是不精确的而且还会卡顿,引入额外的动画封装库也并非对性能敏感的业务适用。

Web Animation API 的历史也应该有几年了,但是每当做动画效果时,笔者就是依赖各种库,很少想着去原生实现,最终造成了我们的项目各种依赖库,体积也不断变大,性能如何也不得而知,作为前端开发的我们多么希望原生的JS去支持通用的动画解决方案, Web Animation API 可能就是一个不错的解决方案。

W3C 提出 Web Animation API(简称 WAAPI)正缘于此,它致力于集合 CSS3 动画的性能、JavaScript 的灵活、动画库的丰富等各家所长,将尽可能多的动画控制由原生JS脚本实现,并添加许多 CSS 不具备的变量以及其它属性的控制。它为我们提供了一种通用语言来描述DOM元素的动画,关于这个API的详细介绍,可以参照MDN的这篇文档,链接地址:

https://developer.mozilla.org/en-US/docs/Web/API/Web_Animations_API。

使用Web Animations API,我们可以将交互式动画从样式表移动到JavaScript,将表示与行为分开。我们不再需要依赖DOM的技术,例如编写CSS属性作用于元素以控制方向。为了构建自定义动画库和创建交互式动画,Web Animations API可能是完成工作的完美工具,你无需借助第三方动画库,就可以轻松实现一个效果不错的动画。

为了让大家对这个API有个清晰的认识,笔者在接下来的系列文章里,用五六个例子让大家理解这个API,今天笔者将用此API实现一个随机移动的图片开始进行介绍,比如用这个效果我们可以制作一个随机飘浮移动的广告位,游戏里随机走动的怪物等等,本例中的特点就是为了体现Web Animation API的灵活性和强大性,我没有引用任何第三方类库,比如(JQ)以及也没有使用setTimeout和requestAnimationFrame()函数。

本篇文章阅读时间预计5分钟。

01

动画效果

开始前,我们先来看看完成后的动画效果,示例如下效果:

02

页面布局

无论图片怎么随机移动,我们都希望在指定的容器里,而不是漫无边际,首先我们在html页面定义容器:

代码语言:javascript
复制
<div id="container">
</div>

接下来定义容器的样式:

代码语言:javascript
复制
body {
    margin: 0;
}
div#container {
    height:500px;
    width:100%;
    background: #C6CEF7;
}
#target {
    position: absolute;
}

03

脚本部分

获取容器

代码语言:javascript
复制
var container = document.getElementById("container");

加载动画

为了更加直观性,我选择一个走动的gif图片,由于图片的加载需要一些时间,为了不破坏动画的连贯性,确保图片加载完了我们在执行动画,相关代码如下:

代码语言:javascript
复制
var target = document.createElement("img");
target.id = "target";
target.onload = function() {
    floatHead();
}
target.src = "walk.gif";
container.appendChild(target);

大家都看到了,onload部分我们加载了floatHead()函数,接下来我们来进行相关实现,此函数主要包含以下功能:创建一个随机位置,计算移动时间,封装移动动画。

随机位置

我们利用Math.floor函数实现了其随机位置的变化(随机函数乘以当前的相对容器的位置属性),示例代码如下:

代码语言:javascript
复制
function makeNewPosition() {
    var containerVspace = container.offsetHeight - target.offsetHeight,
        containerHspace = container.offsetWidth - target.offsetWidth,
        newX = Math.floor(Math.random() * containerVspace),
        newY = Math.floor(Math.random() * containerHspace);
    return [newX, newY];
}

这里的随机位置,我们返回了一个数组,描述的是图片相对容器的位置,即top,left。这里你需要理解offsetHeight,offsetWidth,可理解为div的可视高度或宽度,样式的(height或Width)+(上下padding或左右padding)+(上下border-width或左右border-width)。

计算时间

动画是有时间属性的,我们进行位置的移动,需要花多久时间,假设运动速度为0.1个单位/毫秒。这个函数包含两个数组参数:prev为当前目标的原始X和Y位置,next为移动目标的位置。此函数没有进行进行精确的距离计算,只是判断了x和y轴上移动的距离大小用最大的距离除以速度,示例代码如下:

代码语言:javascript
复制
function velocity(prev, next) {
    var x = Math.abs(prev[1] - next[1]),
        y = Math.abs(prev[0] - next[0]),
        larger = x > y ? x : y,
        speedModifier = 0.1,
        time = Math.ceil(larger / speedModifier);
    return time;
}

封装移动动画

接下来是我们Web Animations API的核心部分,我们使用其核心API在加上上述我们完成的两个函数让其动起来,示例代码如下:

代码语言:javascript
复制
function floatHead() {
    var newPos = makeNewPosition(),
        oldTop = target.offsetTop,
        oldLeft = target.offsetLeft,
        target.animate([
        { top: oldTop+"px", left: oldLeft+"px" },
        { top: newPos[0]+"px", left: newPos[1]+"px" }
    ], {
        duration: velocity([oldTop, oldLeft],newPos),
        fill: "forwards"
    }).onfinish = function() {
        floatHead();
    }
}

该Animation的animate函数有两个参数,一个是KeyframeEffects数组和AnimationEffectTimingPropertiesoptions 的对象。基本上,第一个参数映射到您将放入CSS中的内容@keyframes,你可以想象成css中的@keyframes内容,比如以下代码:

代码语言:javascript
复制
@keyframes emphasis {
    0% {
        transform: scale(1);
        opacity: 1;
    }
    30% {
        transform: scale(.5);
        opacity: .5;
    }
    78.75% {
        transform: scale(.667);
        opacity: .667;
    }
    100% {
        transform: scale(.6);
        opacity: .6;
    }
}

你可以将“{}”里的信息顺序依次放到一个数组对象里;第二个参数包含时间控制 timing,duration 持续时间、iterations 执行次数、direction 动画方向、easing 缓动函数等属性。比如以下代码:

代码语言:javascript
复制
#toAnimate {
    animation: emphasis 700ms ease-in-out 10ms infinite alternate forwards;
}

你还可能注意到我们使用了onfinish事件完成了floatHead函数的反复调用,其实它是Animation的属性用来监听动画完成的事件,如果动画完成继续执行floatHead(),相当不断的递归调用。

04

最终完成的代码

代码语言:javascript
复制
<!DOCTYPE html>
<html lang="en">
<head>
    <style>
        body {
            margin: 0;
        }
        div#container {
            height:500px;
            width:100%;
            background: #C6CEF7;
        }
        #target {
            position: absolute;
        }
    </style>
    <meta charset="UTF-8">
    <title>前端达人示例展示——图片随机移动</title>
</head>
<body>
<div id="container"></div>
<script>
    function makeNewPosition() {
        var containerVspace = container.offsetHeight - target.offsetHeight,
            containerHspace = container.offsetWidth - target.offsetWidth,
            newX = Math.floor(Math.random() * containerVspace),
            newY = Math.floor(Math.random() * containerHspace);
        return [newX, newY];
    }
    function velocity(prev, next) {
        var x = Math.abs(prev[1] - next[1]),
            y = Math.abs(prev[0] - next[0]),
            larger = x > y ? x : y,
            speedModifier = 0.2,
            time = Math.ceil(larger / speedModifier);
        return time;
    }
    function floatHead() {
        var newPos = makeNewPosition(),
            oldTop = target.offsetTop,
            oldLeft = target.offsetLeft;
        target.animate([
            { top: oldTop+"px", left: oldLeft+"px" },
            { top: newPos[0]+"px", left: newPos[1]+"px" }
        ], {
            duration: velocity([oldTop, oldLeft],newPos),
            fill: 'forwards'
        }).onfinish = function() {
            floatHead();
        }
    }
    var container = document.getElementById("container"),
        target = document.createElement("img");
    target.id = "target";
    target.onload = function() {
        floatHead();
    }
    target.src = "walk.gif";
    target.width="200";
    container.appendChild(target);
</script>
</body>
</html>

05

兼容情况

最后聊聊你关心的各浏览器兼容问题,如下所示显示了各个浏览器的兼容情况:

看来好多都是部分支持,没有完全支持,笔者也亲自测试了下,在pc端最新版的谷歌浏览器和Firefox是没有任何问题的可以完美运行,笔者的safari还是运行不起来,在iPhone XS Max下无法运行。

作为一名前端开发者,在移动端大行其道怎么能容忍在手机端没有效果,为了在现代浏览器厂商还没完全跟进到位的时候抢先用上 WAAPI(Web Animation API简称),我们可以选择引入针对 Web Animation API 的 Polyfill 库 [https://github.com/web-animations/web-animations-js],从而在 IE/Firefox/Safari 等浏览器上体验到 WAAPI 的精彩。

因此我们只需要文件里引入以下js,就可以完美体验:

代码语言:javascript
复制
<script src="https://cdn.jsdelivr.net/web-animations/latest/web-animations.min.js"></script>

移动端浏览器,Android 5.0 以上的 Android Browser 和 Chrome for Android 本身就已经支持 WAAPI 了,加上 Polyfill 之后,笔者的手机终于可以看到运行效果了,微信里的QQ内核浏览器也能完美运行,pc端的safari也可以完美运行。可以说是全平台支持了,有了这个库你可以放心大胆的使用了。

好了今天的代码撸完了,js代码还不到50行(注:为了在手机端运行,引入了web-animations.min.js),您可以点击阅读原文预览,笔者亲测在iPhone XS Max运行良好,由于没有其它手机,有待亲们的测试,欢迎到留言区告知。下一篇文章我将用不到20行的原生js代码纯手工撸一个漂亮的时钟,敬请期待...

精彩推荐

十款热门的Vue.js工具和库

vue基础丨新手入门篇(一)

小技巧丨console的用法,不仅仅只有console.log()

动画基础丨点和直线

太惊艳了,这些画都是CSS的杰作!

css基础丨Transforms 属性在实际项目中如何应用?

css基础丨如何理解transform的matrix()用法

css基础丨如何理解Display的属性:None,Block,Inline,Inline-Block

ES6基础丨let和作用域

ES6基础丨const介绍

ES6基础丨默认参数值

ES6基础丨展开语法(Spread syntax)

ES6基础丨解构赋值(destructuring assignment)

ES6基础丨箭头函数(Arrow functions)

ES6基础丨模板字符串(Template String)

ES6基础丨Set与WeakSet

ES6基础丨Map与WeakMap

ES6基础丨Symbol介绍:独一无二的值

ES6基础丨Object的新方法

ES6基础丨迭代器(iterator)

ES6基础丨生成器(Generator)

ES6基础丨你需要知道的Array数组新方法(上)

数据结构基础丨栈简介(使用ES6)

数据结构基础丨队列简介(使用ES6)

JavaScript基础丨前端不懂它,会再多框架也不过只是会用而已!

JavaScript基础丨你真的了解JavaScript吗?

JavaScript基础丨回调(callback)是什么?

JavaScript基础丨Promise使用指南

JavaScript基础丨深入学习async/await

JS加载慢?谷歌大神带你飞!(文末送电子书)

19年你应该关注这50款前端热门工具(上)

19年你应该关注这50款前端热门工具(中)

19年你应该关注这50款前端热门工具(下)

专注分享当下最实用的前端技术。关注前端达人,与达人一起学习进步!

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2019-08-01,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 前端达人 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
容器服务
腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档