响应式扩展_响应式和无限扩展的JS动画

响应式扩展

Back in late 2012 it was not easy to find open source projects using requestAnimationFrame() - this is the hook that allows Javascript code to synchronize with a web browser's native paint loop. Animations using this method can run at 60 fps and deliver fantastic game-like content interactions if one is careful to follow the rules.*

早在2012年末,使用requestAnimationFrame()查找开源项目并不容易-这是允许Javascript代码与Web浏览器的本机绘制循环同步的挂钩。 如果认真遵守规则,使用此方法的动画可以60 fps的速度运行并提供与游戏类似的精彩内容交互。*

Around this time, I had joined Art.com labs and it just so happened that I had a compelling use case for an "iOS-style" interaction model around visual content-stream navigation - one which was responsive across multiple screen sizes and input paradigms (touch, pointer, trackpad). Out of this need came TremulaJS, a Javascript UI component designed to navigate large result sets of visual content with a high degree of UX fluidity.

大约在这个时候,我加入了Art.com实验室,碰巧我有一个引人注目的用例,涉及围绕视觉内容流导航的“ iOS风格”交互模型-该模型可响应多种屏幕尺寸和输入范例(触摸,指针,触控板)。 出于这种需求,出现了TremulaJS ,这是一个Javascript UI组件,旨在以高度的UX流畅性导航大量的可视内容结果集。

This article takes a high-level look at how TremulaJS is organized with a focus on creating responsive, long-running animated interactions using Javascript.

本文从高角度研究了TremulaJS的组织方式,重点是使用Javascript创建响应式,长时间运行的动画交互。

*For those interested in an in-depth look at the fundamentals of a requestAnimationFrame() implementation, Julian Shapiro, creator of velocity.js, has succinctly body-slammed this this topic for your reading pleasure here. I view it as required reading for anyone embarking on a JS animation adventure.

*对于那些对requestAnimationFrame()实现的基础知识有深入了解的人来说,Velocity.js的创建者Julian Shapiro简洁地抨击了此主题,以供您 在这里 阅读 我认为这是从事JS动画冒险的任何人必读的。

TremulaJS:组件概述 (TremulaJS: a component overview)

TremulaJS is composed of five main components - The Scroll Axis, Momentum Loop, Content Grid, Content Box and Grid Projection.

TremulaJS由五个主要组件组成-滚动轴,动量循环,内容网格,内容框和网格投影。

fig1. The Scroll Axis, Scroll Offset and Content Grid as they relate to the TremulaJS view container. This figure shows a grid of discrete content elements which are able to slide (on an axis) across the viewable area. Content outside this area is not rendered.

图。1。 与TremulaJS视图容器相关的“滚动轴”,“滚动偏移”和“内容网格”。 该图显示了一个离散内容元素的网格,这些元素能够(在轴上)在可见区域内滑动。 此区域之外的内容不会呈现。

滚动轴 (Scroll Axis)

TremulaJS enables all kinds of micro-interactions, however, at the end of the day there is only one dimension of navigation and that is the Scroll Offset value. This value is encapsulated by the Scroll Axis object which among other things, manages horizontal and vertical orientation.

TremulaJS支持各种微交互,但是,最终,导航只有一个维度,即“滚动偏移”值。 该值由“滚动轴”对象封装,其中,“滚动轴”对象管理水平和垂直方向。

动量循环 (Momentum Loop)

The momentum loop regulates the momentum value throughout the system. It is the sum of various sub-component outputs including: an internal momentum clock, various conditional dampening functions linked to Scroll Axis states and a user interaction event handler. On each animation frame it returns an instantaneous output value of momentum used to calculate the the scroll offset position.

动量循环调节整个系统的动量值。 它是各种子组件输出的总和,包括:内部动量时钟,链接到“滚动轴”状态的各种条件衰减功能以及用户交互事件处理程序。 在每个动画帧上,它返回用于计算滚动偏移位置的动量的瞬时输出值。

内容网格 (Content Grid)

The Content Grid is an abstraction model of content boxes arranged on a configurable XY grid. All content added to this grid is proportionally scaled along the Cross Axis to maintain normalized row (or column) dimensions.

内容网格是排列在可配置XY网格上的内容框的抽象模型。 所有添加到此网格的内容都将沿“横轴”按比例缩放,以保持标准化的行(或列)尺寸。

On every frame, as momentum moves the Content Grid to a new position along the scroll axis, the Content Grid updates it's child Content Boxes with new relative positions. This is the abstraction that gives us opportunities to extend the painting process and make cool stuff happen…

在每帧上,随着动量沿滚动轴将内容网格移动到新位置,内容网格将使用新的相对位置更新其子内容框。 是使我们有机会扩展绘画过程并使酷事发生的抽象……

内容框 (Content Box)

A Content Box is created for each unit of content appended to the Content Grid. A Content Box has width, height, an optional HTML template, and an optional main image which (if provided) is preloaded and transitioned on-screen by a CSS class. This should not be an unfamiliar paradigm to a web developer.

将为附加到“内容网格”的每个内容单元创建一个“内容框”。 内容框具有宽度,高度,可选HTML模板和可选的主图像(如果提供),它们由CSS类预加载并在屏幕上转换。 对于Web开发人员来说,这不应该是一个陌生的范例。

The interesting bit starts here: Each content block also maintains various primitive waveform values corresponding to it's own on-screen scroll progress. These waveforms can be mapped to animate any aspect of an Content Box DOM element in time and space. Let's zoom in on that with a diagram…

有趣的地方从这里开始:每个内容块还维护与其原始屏幕滚动进度相对应的各种原始波形值。 可以将这些波形映射为在时间和空间上为Content Box DOM元素的任何方面设置动画。 让我们用一个图放大一下……

fig2. Linear progression of a content block across the screen with a "ramp" waveform shown underneath.

图2。 内容块在屏幕上的线性变化,下方显示“斜坡”波形。

In the figure above, we can follow a Content Block as it's moved across a screen and imagine that the output of our ramp waveform is mapped to a function which updates a CSS translateX() property.

在上图中,当内容块在屏幕上移动时,我们可以跟随它,并想象将斜坡波形的输出映射到更新CSS translateX()属性的函数。

This however is not the default behavior - it's a little more complex than that. Here is a simplified example of the default function called on a Content Box during a render cycle…

但是,这不是默认行为-比这要复杂一些。 这是渲染周期中在内容框上调用的默认函数的简化示例……


function updateContentBoxElementProperites(x,y) {
  var ramp = this.waveforms.headRamp,
    xo=x,
    yo=y,
    zo=0;
    
  this.e.style.transform = 'translate3d(' + xo + 'px,' + yo +'px, ' + zo + 'px)';
  //this.e.style.opacity = ramp;
  this.pPos = [x,y];//cache the current position in the Content Box model
}

This function is called when it's time to reposition our Content Box and we can see here that it is passed new coordinates. x & y are absolute values corresponding to the geometry of our TremulaJS view, these values are provided to the function by the Content Grid, which has knowledge of all Content Blocks and is able to efficiently crunch all the positions of all Content Boxes on the grid. The above function is then called on every Content Box on every animation frame.

当需要重新定位内容框时,将调用此函数,并且在此处可以看到它传递了新坐标。 x和y是与TremulaJS视图的几何形状相对应的绝对值,这些值由Content Grid提供给函数,Content Grid具有所有Content Block的知识,并且能够有效地处理网格上所有Content Box的所有位置。 然后,在每个动画帧的每个内容框上调用上述函数。

Notice the commented opacity assignment. If we were to uncomment this we would see our content block fade-in as it moved from left to right (or fade-out as it moved from right to left.) This works because our ramp value is a derivative value (between 0 and 1) tied to a Content Box's scroll progress across our TremulaJS view. Conveniently, this.e.style.opacity is expecting a number between 0 and 1.

注意注释的不透明度分配。 如果要取消注释,我们将看到内容块在从左向右移动时淡入(或在从右向左移动时淡出)。这是因为我们的渐变值是微分值(介于0和1)与内容框在TremulaJS视图中的滚动进度相关。 方便地, this.e.style.opacity期望一个介于01之间的数字。

News Flash: turns out Bézier paths are super responsive

新闻快讯:事实证明Bézier路径具有超级响应能力

看看网格投影 (A look at the Grid Projection)

There is a fifth component belonging to TremulaJS which enables us to take elements of a content grid and project that along a Bėzier path. Unsurprisingly, this is called a grid projection.

TremulaJS属于第五个组件,它使我们能够采用内容网格的元素并将其沿着Bėzier路径投影。 毫不奇怪,这称为网格投影

So to recap: As shown in the previous example, we are looking at a Content Box function that is executed on on every frame. This function is passed instantaneous x&y values corresponding to the Content Box's own orientation in the TremulaJS view at a particular point in time. This function is also passed several primitive waveform values corresponding to it's own on-screen scroll progress. It is at this point where we are able to remap an arbitrary Bezier path to virtually any CSS property. Let's take another look at the example above, except we will change the vertical position of our Content Box by replacing the absolute x&y position with one generated from our Bézier function.

回顾一下:如前面的示例所示,我们正在研究在每个帧上执行的Content Box函数。 该函数在特定时间点在TremulaJS视图中传递与Content Box自身方向相对应的瞬时x&y值。 该函数还会传递几个与其自身的屏幕滚动进度相对应的原始波形值。 在这一点上,我们可以将任意Bezier路径重新映射到几乎所有CSS属性。 让我们再看一下上面的示例,除了我们将通过用Bézier函数生成的x&y绝对位置替换绝对位置来更改内容框的垂直位置。

fig3. Linear progression of a content block across a view with a bézier waveform shown underneath. The Bézier output is now mapped to the x&y position of our content box within the TremulaJS view.

图3。 内容块在视图上的线性级进,下方显示贝塞尔波形。 现在,Bézier输出被映射到TremulaJS视图中内容框的x&y位置。


var bezierArcPath = [
  {x:0,y:0},
  {x:0,y:1},
  {x:1,y:1},
  {x:1,y:0}
];

function updateContentBoxElementProperites(x,y,env) {

  var path = bezierArcPath;

  var 
    areaX = env.viewDims[0],
    areaY = env.viewDims[1],
    ramp = this.waveforms.tailRamp,
    xo=x,
    yo=y,
    zo=0;

  var xyFactor = [
    areaX,
    areaY
  ];

  var scaledPath = env.factorPathBy(path,xyFactor);
  
  var p = jsBezier.pointOnCurve(cubicBezier, ramp);
  var g = jsBezier.gradientAtPoint(cubicBezier, ramp);
  
  xo = p.x - (this.dims[0]*.5);
  yo = areaY - p.y - (this.dims[1]*.5);
  zo = 0;

  this.e.style.transform = 'translate3d(' + xo + 'px,' + yo +'px, ' + zo + 'px)';

  this.pPos = [x,y];
}


Please note: variable names in these examples have been changed/cleaned-up to enhance high-level understanding - actual code is not this pretty. Fork and improve!

请注意:这些示例中的变量名称已经过更改/整理,以增强高级理解-实际代码并非如此。 叉和改善!

In this example we have added a few methods to help implement our Bėzier transforms. First, let's look at env.factorPathBy(path,xyFactor). The responsive power of this utility function is great - it allows us to define any bounding box area (in this case, the current dimensions of the TremulaJS view), and scale our path in two dimensions such that the path will fit the box. What is returned is prescaled, ready-to-use path coordinates.

在此示例中,我们添加了一些方法来帮助实现Bėzier变换。 首先,让我们看一下env.factorPathBy(path,xyFactor) 。 该实用程序功能的响应能力非常强-它使我们能够定义任何边界框区域(在本例中为TremulaJS视图的当前尺寸),并在两个维度上缩放路径,以使该路径适合该框。 返回的是预先缩放的,随时可用的路径坐标。

Next in our chain is jsBezier.pointOnCurve(cubicBezier, ramp). Which takes our scaled path and our current ramp output as parameters. Our transformed x&y values are returned. Many thanks here go to Simon Porritt for porting classical Bėzier math to JS and posting the jsBezier library to gitHub!

我们链中的下一个是jsBezier.pointOnCurve(cubicBezier, ramp) 。 它以缩放路径和当前斜坡输出为参数。 返回我们转换后的x&y值。 非常感谢Simon Porritt将经典的Bėzier数学移植到JS并将jsBezier库发布到gitHub!

The rest should look familiar enough. We then make some small adjustments to x&y so that our content is positioned from its center origin.

其余的应该看起来足够熟悉。 然后,我们对x&y进行一些小的调整,以使内容从其中心原点开始定位。

但是,等等,还有更多! (只是不在本文中...) (But wait, theres more! (Just not in this article...))

Beyond this example, there are so many animations that can be created from these basic building blocks. For example, jsBezier.gradientAtPoint(cubicBezier, ramp) gives us instantaneous tangent values as content moves along our path, enabling coordinated content rotation among other possibilities. There is also the z axis and a primitive triangle waveform which enables depth effects (making content appear closer as it moves into the center of our view.

除了该示例之外,还可以从这些基本构建块创建许多动画。 例如,当内容沿着路径移动时, jsBezier.gradientAtPoint(cubicBezier, ramp)为我们提供了瞬时切线值,从而实现了协调的内容旋转。 还有z轴和原始三角形波形,可实现深度效果(使内容移入视图中心时显得更近。

Curves can just as easily be used to produce easing effects or to keep our content on a single responsively positioned axis.

曲线可以很容易地用于产生缓和效果或将我们的内容保持在单个响应轴上。

Another TremulaJS feature is Content Box momentum. When enabled, the content grid does not immediately update a Content Box's DOM as the Scroll Offset changes. Instead, the Content Box manages its own momentum value relative to its relationship of the motive force location (e.g. your finger or mouse pointer over the grid) - this can produce interesting content level momentum effects.

TremulaJS的另一个功能是Content Box动量。 启用后,随着“滚动偏移”的更改,内容网格不会立即更新内容框的DOM。 取而代之的是,内容盒根据其与动力位置的关系(例如,手指或鼠标指针在网格上)的关系来管理自己的动量值-这可以产生有趣的内容级别动量效果。

For those who are interested, there is a great path editing tool here...

对于那些感兴趣的人,这里有一个很棒的路径编辑工具...

https://www.desmos.com/calculator/d1ofwre0fr

https://www.desmos.com/calculator/d1ofwre0fr

翻译自: https://davidwalsh.name/responsive-scalable-animations

响应式扩展

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值