惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

G
Google Developers Blog
S
Schneier on Security
The Hacker News
The Hacker News
P
Proofpoint News Feed
Spread Privacy
Spread Privacy
L
LINUX DO - 热门话题
L
Lohrmann on Cybersecurity
I
Intezer
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
www.infosecurity-magazine.com
www.infosecurity-magazine.com
Schneier on Security
Schneier on Security
Security Latest
Security Latest
AWS News Blog
AWS News Blog
B
Blog RSS Feed
Microsoft Security Blog
Microsoft Security Blog
有赞技术团队
有赞技术团队
博客园 - 叶小钗
The Last Watchdog
The Last Watchdog
O
OpenAI News
月光博客
月光博客
Hacker News: Ask HN
Hacker News: Ask HN
阮一峰的网络日志
阮一峰的网络日志
S
Security @ Cisco Blogs
Google Online Security Blog
Google Online Security Blog
云风的 BLOG
云风的 BLOG
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
Latest news
Latest news
P
Palo Alto Networks Blog
Last Week in AI
Last Week in AI
M
MIT News - Artificial intelligence
Google DeepMind News
Google DeepMind News
P
Proofpoint News Feed
C
CERT Recently Published Vulnerability Notes
Apple Machine Learning Research
Apple Machine Learning Research
U
Unit 42
PCI Perspectives
PCI Perspectives
博客园 - 聂微东
SecWiki News
SecWiki News
宝玉的分享
宝玉的分享
Forbes - Security
Forbes - Security
H
Heimdal Security Blog
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
Hugging Face - Blog
Hugging Face - Blog
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
T
Troy Hunt's Blog
博客园 - 三生石上(FineUI控件)
Application and Cybersecurity Blog
Application and Cybersecurity Blog
罗磊的独立博客
WordPress大学
WordPress大学
D
Darknet – Hacking Tools, Hacker News & Cyber Security

oida.dev | TypeScript, Rust

TypeScript's `erasableSyntaxOnly` Flag Unsafe for work Tokio: Macros Tokio: Channels Tokio: Getting Started Network Applications on the Tokio Stack Remake, Remodel, Reduce. The `never` type and error handling in TypeScript 5 Inconvenient Truths about TypeScript Refactoring in Rust: Introducing Traits Refactoring in Rust: Abstraction with the Newtype Pattern Announcing the TypeScript Cookbook TypeScript: Iterating over objects The road to universal JavaScript 10 years of oida.dev Rust: Tiny little traits The TypeScript converging point How not to learn TypeScript Getting started with Rust Introducing Slides and Coverage TypeScript: The humble function overload TypeScript + React: Children types are broken TypeScript: In defense of any Rust: Enums to wrap multiple errors Dissecting Deno Error handling in Rust TypeScript: Unexpected intersections Upgrading Node.js dependencies after a yarn audit TypeScript: Array.includes on narrow types TypeScript + React: Typing Generic forwardRefs shared, util, core: Schroedinger's module names Learning Rust and Go TypeScript: Narrow types in catch clauses TypeScript: Low maintenance types Tidy TypeScript: Name your generics Tidy TypeScript: Avoid traditional OOP patterns Tidy TypeScript: Prefer type aliases over interfaces Tidy TypeScript: Prefer union types over enums My new book: TypeScript in 50 Lessons Go Preact! ❤️ this in JavaScript and TypeScript TypeScript and ECMAScript Modules TypeScript + React: Why I don't use React.FC TypeScript + React: Component patterns TypeScript: Augmenting global and lib.dom.d.ts Vite with Preact and TypeScript TypeScript: Union to intersection type 11ty: Generate Twitter cards automatically Are large node module dependencies an issue? TypeScript: Variadic Tuple Types Preview TypeScript: Improving Object.keys Remake, Remodel. Part 4. TypeScript + React: Typing custom hooks with tuple types TypeScript: Assertion signatures and Object.defineProperty TypeScript: Check for object properties and narrow down type Boolean in JavaScript and TypeScript void in JavaScript and TypeScript Symbols in JavaScript and TypeScript Why I use TypeScript TypeScript + React: Extending JSX Elements TypeScript: Validate mapped types and const context TypeScript: Match the exact object shape TypeScript: The constructor interface pattern Streaming your Meetup - Part 4: Directing and Streaming with OBS Streaming your Meetup - Part 3: Speaker audio Streaming your Meetup - Part 2: Speaker video Streaming your Meetup - Part 1: Basics and Projector TypeScript and React Guide: Added a new styles chapter TypeScript and React Guide: Added a new render props chapter TypeScript and React: Styles and CSS TypeScript and React TypeScript and React Guide: Added a new prop types chapter TypeScript without TypeScript -- JSDoc superpowers TypeScript: Mapped types for type maps JAMStack vs serverless web apps The Unsung Benefits of JAMStack Sites TypeScript: Ambient modules for Webpack loaders My most favourite talks in 2018 TypeScript and React Guide: Added a new context chapter TypeScript: Built-in generic types TypeScript: Type predicates JSX is syntactic sugar TypeScript and React Guide: Added a new hooks chapter Getting your CfP application right FAQ on our Angular Connect Talk: Automating UI development TypeScript and Substitutability Debugging Node.js apps in TypeScript with Visual Studio Code From Medium: Deconfusing Pre- and Post-processing From Medium: PostCSS misconceptions Saving and scraping a website with Puppeteer Cutting the mustard - 2018 edition Wordpress as CMS for your JAMStack sites My most favourite podcast episodes in 2017 My most favourite talks in 2017 My most favourite books in 2017 The Best Request Is No Request, Revisited Not so hidden figures - Organizing ScriptConf My podcast journey to ScriptCast Grid layout, grid layout everywhere! #scriptconf and #devone
Basic SVG path tweening with SMIL
2013-07-16 · via oida.dev | TypeScript, Rust

Sorry, your browser does not support SVG animations with SMIL.

I'm working on a tribute to one of my childhood heroes, the Caped Crusader, the Dark Knight, the world's greatest detective: Batman. And when I say childhood hero, I do mean a hero to this day. Anyhow, inspired by an EPS file i got over on DeviantArt, I wanted to create a history of his emblems from the very first to the very last, spanning all 73 years, much like this now infamous video did.

First I had the idea of just fading over the logos, but that's actually kinda boring, so I went back to a rad idea I used once back then when Macromedia Flash 4 was still in its early days: Tweening! (well, just like in the video, no?)

After a little research, I stumbled upon two ways to do it: Animating SVG with RaphaëlJS, a JavaScript library for cross-browser SVG, or using the very powerful SMIL for SVG animations.

All right! To the Batcave, Robins!

A short thought on RaphaëlJS

We already have some experience with RaphaëlJS in our company. We used the library to create parts of Adidas Customize to achieve recolorable, complex formed widgets on IE7 and IE8.

The library also allows to animate between paths, and does it in a very interesting, jQuery-like way: Instead of using SMIL, RaphaëlJS interpolates path points between the start and ending state and constantly updates the path inside your SVG. I was stunned by the complexity of this rather powerful algorithm, but looking at it from a performance point of view ... nah, you get the same issues you love to hate from jQuery.

RaphaëlJS is good if you don't want to delve to deeply into drawing programs or SVG source code, and I used it mainly to apply certain transformations on exiting SVG paths, and then copying the new SVG result. But for my tribute page I dropped it completely and just used it as a fallback for IE, because SMIL is still not implemented and looking at the current preview of IE11, will not be landing there for quite some while.

But the main reason for me to use SMIL was a rather clear one: Why using an 80kb JavaScript library if I can do everything with native means?

The first animation

My work is based on a great animation done by Tavmjong Bah. In his blog post he give additional information on how he actually implemented it. Some important parts were: The SVG paths you want to transform have to be in the same pattern, otherwise you don't get any animation at all:

Not even those funky effects we know from Flash back then, which is one of the main advantages of RaphaëlJS: The algorithm interpolating between two paths might lead to quirky results, but is nonetheless bloody good!

Check out this Pen!

Anyhow, I did want to stick to SMIL, but even by using Tavmjongs data I wasn't able to recreate one transition between two bats. It took me some time to realize how Tavmjong was implementing his animation. Mostly because I didn't take a good look at the values. The <animate>-element is pretty straightforward, but the values do need some explanation: To create an animation from path A to B, the values inside the element have to feature both paths, separated by a semicolon. So if you want a transition from Figure A to B, you first have to include the path in your <path>-element, and then again as the first value tuple in your animation:

<!-- The 'd' in path is the first bat -->
<path
d="M 256,213 C 245,181 206,187 ..."
fill="#000000">

<!-- The 'values' include the first
as well as the second bat -->

<animate dur="2s"
repeatCount="indefinite"
attributeName="d"
values="M 256,213 C 245,181 206,187 ... Z;
M 212,220 C 197,171 156,153 ... Z;"
/>

</path>

Result

Actually, I lied a little bit. You don't need to include the path inside the "d" attribute of the <path>-element. The animation will work fine even without it. But: if you include the path data directly you can do some more, event-based stuff with your SVG. But more on that later. First, check on some of the attributes of the <animate> element.

Parameters

Some parameters are already visible in the example above:

  • dur is short for "duration" and defines exactly that. Use any value in seconds for that.
  • Similar, repeatCount allows us to define how often the animation is going to be repeated. It takes any number, or indefinite for endless repeat.
  • attributeName defines which attribute of the original element is going to be animated. Since the <animate> tag can be used on a multitude of SVG elements, this parameter has a multitude of possibilities. We use the "d" (very short for "path data"), since this is the parameter of the original <path> element.
  • Last, but not least we defined a set of values

We are not done with that. One thing you might realize is that the animation always jumps back to it's initial frame (which is why we also need to define the original path in the parent <path> element). To make sure that the ending state is preserved, we add another attribute called fill and set its value to freeze. In other elements, fill is used to define the filling color, in animation it's the state at the end.

<animate
dur="2s"

fill="freeze"

repeatCount="1"
attributeName="d"
values="..." />

Result

Trigger the animation by clicking or tapping on it.

Triggers

As you've seen, animations can be triggered on certain actions. Use the begin attribute to define the interaction or property which starts the animation, as well as end to define the interaction which should stop it.

And this is where this stuff becomes really good, as you can add at least some control to your animation. You can either use DOM events for that, like click (as shown in the example above) or mouseover, but you also can use time constraints to apply a certain delay:

<!-- Triggers the animation after 1s -->
<animate
dur="2s" repeatCount="indefinite"
attributeName="d"

begin="1s"

values="..."
/>

<!-- Triggers the animation when clicking
on the element -->

<animate
dur="2s" repeatCount="indefinite"
attributeName="d"

begin="click"

values="..."
/>

<!-- Triggers the animation on mouseover,
stops it on mouseout -->

<animate
dur="2s" repeatCount="indefinite"
attributeName="d"

begin="mouseover"
end="mouseout"

values="..."
/>

<!-- Triggers the animation on click,
stops it also on click -->

<animate
dur="2s" repeatCount="indefinite"
attributeName="d"

begin="click"
end="click"

values="..."
/>

These parameters take almost any input based DOM event, but with one very special constraint: The SVG data has to be embedded in the DOM. If you have your SVG in a file and are referencing it in an image tag or whatever, the DOM events won't trigger.

Trigger events can be expanded further by not only using the DOM event of an element itself, but also by referencing to an event by another element. For instance, begin="button.click" allows us to trigger the animation once a certain element with the id of button has been clicked.

This gives us a multitude of possibilities. Look at that the following example:

<svg>
<path d="...">
<animate
dur="2s" fill="freeze"
begin="click" id="anim1"
values="..." />

</path>
</svg>

<svg>
<path d="...">
<animate
dur="2s" fill="freeze"
begin="anim1.begin"
values="..." />

</path>
</svg>

Here we start the second animation once the first one has already started.

Result

Click on the left bat to see the magic happen.

Events

Sorry, your browser does not support SMIL events

SMIL supports some events to add additional control with JavaScript to your animation needs. Unfortunately, at the moment animation events are just implemented by Firefox (and pre-Blink Opera ...). I wanted to use this method not only to show some elements once the animation is done, but also to keep the state ready for the next animation, by removing the <animate>-element and changing the original path.

I dropped this idea due to browser constraints, tough it would've been easy: Simply add the callback into your markup, or use addEventListener to achieve the same.

<animate
dur="2s" fill="freeze" begin="click"
repeatCount="1"
attributeName="d"

onend="cry()"

values="..." >

function cry() {
alert("I'M BATMAN");
}

Other events include onbegin which is obviously triggered when the animation starts, and onrepeat, which counts the number of interations the animation has run and fires every time one iteration is complete.

Result

Start the animation by clicking or tapping the bat!

Again, this will work only if the SVG is included directly in your DOM.

Feature test

As you all know, we just should feature detect to check if we're able to use SMIL. However, it might be that you get a false positive in IE9, according to this (somewhat old) Modernizr issue, so be aware!

With that one you can detect SMIL:

return !!document.createElementNS('http://www.w3.org/2000/svg', 'animate');

Place it in a try-catch block! To check for event callbacks, this is how it should work.

var el = document.createElementNS('http://www.w3.org/2000/svg', 'animate');
return !!el.onend;

However, not even Firefox provides interfaces in their DOM API. This is a workaround: Add an almost empty SVG markup to your DOM and have it call a function once it starts. In this function, set your bools or classes, or whatever you like or need.

function smilEventsSupported() {
//set classes or whatever
}

//can be any element
document.body.innerHTML += '<svg width="0" height="0">'
+ '<path d="m 1,1 1,1 0,0">'
+ '<animate values="m 0,0 0,0 0,0"'
+ 'attributeName="d" begin="0s" repeatCount="0"'
+ 'onbegin="smilEventsSupported()"/>'
+ '</path></svg>'

Bottom line

This blog entry is based on about a weekend of research on that topic, fooling and playing around with values and constantly checking the specification. And I'm sure that I just scratched the surface! I stumpled upon parameters such as keytimes, keyspines or calcMode, which I didn't read in depth. Also the possibility of an animationPath is available, where I've no bloody clue how they can be created whatsoever. SVG animations are a beast of their own, and very powerful once you understand them.

Use is limited, tough. Microsoft has no plans of including SMIL in their current versions of IE. The spec has been out there for a while, but even the preview version of IE11 shows no sign of implementation. Furthermore, if you want to use that stuff on mobile browsers, be aware that performance might be below your expectations. Even iOS Safari, which is still one of the most performant mobile browsers out there, has a clear performance issue when it comes to basic tweening. It's not as bad as animating with RaphaëlJS, tough, because there you just won't get any animation at all.

Related Articles