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

推荐订阅源

J
Java Code Geeks
GbyAI
GbyAI
Google DeepMind News
Google DeepMind News
Jina AI
Jina AI
B
Blog
aimingoo的专栏
aimingoo的专栏
酷 壳 – CoolShell
酷 壳 – CoolShell
T
The Blog of Author Tim Ferriss
Last Week in AI
Last Week in AI
月光博客
月光博客
H
Help Net Security
V
Visual Studio Blog
量子位
A
About on SuperTechFans
博客园 - Franky
人人都是产品经理
人人都是产品经理
N
Netflix TechBlog - Medium
云风的 BLOG
云风的 BLOG
雷峰网
雷峰网
Martin Fowler
Martin Fowler
Microsoft Security Blog
Microsoft Security Blog
博客园 - 叶小钗
P
Proofpoint News Feed
MongoDB | Blog
MongoDB | Blog

Red Blob Games: latest blog posts

Red Blob Games: Differential heuristics Red Blob Games: Responsive design calculator Red Blob Games: Academic citations Red Blob Games: Highlighting interactive code blocks Red Blob Games: Optimizing page size Red Blob Games: Writing a guide to SDF fonts Red Blob Games: URLs with trailing punctuation Red Blob Games: What I did in 2025 Red Blob Games: Goodbye Sass Goodbye Sass Red Blob Games: RotMG map seeds Red Blob Games: Mapgen4 Mapgen4's use of WebGL2 Red Blob Games: Mapgen4 river shader Red Blob Games: Mapgen4 renderer Red Blob Games: Harnessing ChatGPT hallucinations Red Blob Games: Let Let's write a search engine, part 2 of 2 Red Blob Games: Let Let's write a search engine, part 1 of 2 Red Blob Games: Hexagon conversions Red Blob Games: Mapgen4 trade routes Red Blob Games: De-optimizing mapgen4 Red Blob Games: Hexagon spiral coordinates Red Blob Games: Thoughts on Flash Red Blob Games: What I did in 2024 Red Blob Games: SDF Halos SDF Halos Red Blob Games: SDF headless tests, part 2 Red Blob Games: SDF headless tests, part 1
Red Blob Games: Hexagon page animations
2024-12-17 · via Red Blob Games: latest blog posts
Blog post: 16 Dec 2024

When I first wrote my hexagon guide[1] in 2013 I used d3.js[2], which has a nice animation system. I had some trouble with CSS transitions in SVG back then, so I was using Javascript transitions using SVG attributes instead of CSS. This looked something like:

d3.select(".grid")
  .transition()
  .attr("transform", "rotate(30)");

That would rotate the grid from flat-topped to pointy-topped, but the text would be rotated too:

Screenshot of a hexagonal grid rotated 30 degrees into 'pointy top' orientation, with the text rotated too
Text rotates with the diagram

The solution was to undo the rotation on the <text> nodes:

d3.select(".grid text")
  .transition()
  .attr("transform", "rotate(-30)");
Screenshot of the same hexagonal grid rotated 30 degrees, with the text rotated back to be upright
Text re-rotated to undo the diagram rotation

When I rewrote the hexagon guide in Vue in 2018[3], I wrote my own animations that set a Javascript value between 0 and 1, and turned that into an angle between 0° and 30°. I then applied that angle to both the grid and the text:

<svg>
  <g class="grid" :transform="rotationTransform">
    <g v-for="hex in grid">
      <text :transform="undoRotationTransform" />
    </g>
  </g>
</svg>

In both of these implementations, I used a Javascript value to change SVG attributes. As an optimization, I limited the animation to diagrams that were visible. Diagrams that weren’t visible would “snap” to the final position.

During the rewrite I was hoping to simplify the implementation a little bit.

SVG2 added a feature in 2016 specifically to solve the problem of rotated text: vector-effect: non-rotation[4]. Caniuse shows it being supported[5], and the MDN page shows it as supported[6]. However when I tested it back in 2018, and again in 2024, it didn’t work anywhere[7]. There’s a Chrome Bug[8] that says the non-rotation value is unimplemented since 2017, and a Firefox bug[9] that says they won’t implement it unless Chrome does (probably because developers won’t use all the features Firefox has implemented that Chrome didn’t). So I can’t use this feature.

I wanted to use CSS transitions[10] back then, but browser features and bugs back then limited what I could do. Since 2018, browsers have adopted lots of new features, and many bugs have been fixed. I decided to try using CSS transitions again.

But why CSS transitions? Partly because it’s easier to program, and partly because it should be faster. With Javascript transitions, I can start from something like this:

<svg>
  <g transform="rotate(0)">
    <text transform="rotate(0)">
    <text transform="rotate(0)">
    <text transform="rotate(0)">
    <text transform="rotate(0)">
    <text transform="rotate(0)">
    <text transform="rotate(0)">

  </g>
</svg>

and want to turn it into this:

<svg>
  <g transform="rotate(30)">
    <text transform="rotate(-30)">
    <text transform="rotate(-30)">
    <text transform="rotate(-30)">
    <text transform="rotate(-30)">
    <text transform="rotate(-30)">
    <text transform="rotate(-30)">

  </g>
</svg>

During the transition, I have to change all of those numbers every animation frame. As of today, I have 2145 elements ✕ 60frames/sec ✕ 0.5sec = 64,350 html updates to run the full animation. With CSS transitions, I can do this instead:

<svg>
  <g class="rotate-by-0">
    <text class="unrotate">
    <text class="unrotate">
    <text class="unrotate">
    <text class="unrotate">
    <text class="unrotate">
    <text class="unrotate">

  </g>
</svg>

and then change it to

<svg>
  <g class="rotate-by-30">
    <text class="unrotate">
    <text class="unrotate">
    <text class="unrotate">
    <text class="unrotate">
    <text class="unrotate">
    <text class="unrotate">

  </g>
</svg>

just once, not every frame. And I only have to change the class at the top, not all the individual <text> elements. That means I have 39 html updates to run the full animation … down from 64,350!

First, I needed to make sure CSS transitions would work. I designed a CSS easing function and tested it with an online playground[11]. It worked across the browsers I test on.

I implemented this change in small steps and tested each step across browsers:

  1. Switch from transform attribute to transform style, still controlled via Javascript.
  2. Refactor styles to have the diagram rotate and the text unrotate.
  3. Switch from inline style to class, moving the style rules to the global CSS.
  4. Add CSS transition rule, transition: transform 0.5s; transition-timing-function: cubic-bezier(0.5, -0.2, 0.5, 1.2);.
  5. Some elements couldn’t use CSS transitions so wrote a Javascript approximation to it, along with a test to make sure the CSS and Javascript versions stay in sync.

I did run into one bug, with Safari this time. The animations should all play when I change the CSS class. In Safari, for elements not currently on the screen, it would start the animation when the element is scrolled into view. That’s too late. It should have finished by then. To work around this bug, I used IntersectionObserver to trigger the animation only on visible elements.

I’m glad I was able to simplify the implementation. I hope the animation runs smoother on more devices, but on my computers I wasn’t able to tell.