Jump to content
Search Community

Coordinating opacity and viewBox tweens

swampthang test
Moderator Tag

Warning: Please note

This thread was started before GSAP 3 was released. Some information, especially the syntax, may be out of date for GSAP 3. Please see the GSAP 3 migration guide and release notes for more information about how to update the code to GSAP 3's syntax. 

Recommended Posts

I'm working on a tool that will build kinetic type and really struggling with the timing of things. Trying to emulate this kind of thing - http://aescripts.com/typemonkey/ 

I'm using a foreignobject in a main SVG with div's containing a span wrapping each word. I'm looping through each word snagging the left, top, width and height and using those variables to fill the main SVG's viewBox attribute using this function:

function buildTweens() {

  var time = 0;

    var x = parseInt($(this).position().left),
        y = parseInt($(this).position().top),
        wd = parseInt($(this).width()),
        ht = parseInt($(this).height()),
        stageTween, wordTween;
    stageTween = TweenMax.to(stage,1,{
      attr:{ viewBox: `${x} ${y} ${wd} ${ht}` }
    wordTween = TweenMax.to($(this),1,{opacity: 1});
    time += 1.5;

I set the opacity of each word to 0 initially and then tween the viewBox to fill the screen with each word. I just used a time variable as a counter and am doing tl.add for the viewBox tween and the fade starting at the same time then waiting a half second to start the next viewBox/word tween combo. 


I've tried lots of ways to time this but it gets wonky on me real fast. Was trying to tween over to the next word and then fade the word in, leave it there for a half second (really want the time it stays to be variable ultimately) and then move to the next one. 


Probably just having a bit of a brain-f... er, uh, brain-cramp but it seems like the opacity tween always starts before the viewBox tween causing the timing to get off. Also seeing a flash of the un-zoomed text. 


One thing to note is that if you set opacity to 1 in the stylesheet and comment out the opacity tween, everything runs smoothly. It's like adding the opacity tween forces the viewBox tween to wait for it to finish.


(Also, this is being created in Electron so I'm not worried about this not working in Firefox.)


Any idea what's going on?


By the way, the codepen I started with (and am currently working on with a form element and dynamically created content) is here. 

See the Pen PbQNXz by swampthang (@swampthang) on CodePen

- It's more involved so I created the one listed as the codepen for this post as a much simpler, pared down version.

See the Pen eBKjyy?editors=1010 by swampthang (@swampthang) on CodePen

Link to comment
Share on other sites

Not sure.. but it looks like an issue with the viewBox. Using a normal value like this you can see the text fade in.

attr:{ viewBox: `${0} ${0} ${1000} ${1000}` }

I find dealing with the viewBox to be rather difficult at times. I'd just skip it, and build some type of camera system. Not too hard, actually. Assuming there is no viewBox, you can zoom into a point like this. If you are using a viewBox, you will need to convert stuff into a local coordinate space using the transform method you did here.

TweenLite.to(foo, 1, {
 x: -pointX * (scale - 1),
 y: -pointY * (scale - 1),
 scale: scale

Demo of that using one of PointC's demos...

See the Pen YpjKxE by osublake (@osublake) on CodePen

And to pan, you just move everything in the opposite direction you want to pan to. Here's a simple panning demo that follows the mouse.

See the Pen 4160082f5a86a3cd0410fb836a74fa68?editors=0010 by osublake (@osublake) on CodePen

And that's all a camera system really is. You pan to something, like the coordinates of your words. 
I put together a really simple DOM based camera that can do panning and zooming. No rotation, but it's possible using the geometry classes I included. It's not actually using all that code. 

See the Pen 9602483c8a083be3cd101c98591890a8?editors=0010 by osublake (@osublake) on CodePen



  • Like 3
Link to comment
Share on other sites

I'm working on setting up a dynamic way of creating a rotation grid for words at random sizes. The goal would be to create something like this...


See the Pen OVgoQV?editors=1010 by simonswiss (@simonswiss) on CodePen


... only, to be able to enter some text into a textarea and have it produce the same kind of effect. Wanted to dynamically create that using TweenMax.set for rotation matching the coords from each previous phrase or word but it's tricky to match up coords.


Know of a rotation grid system? Or has anyone created something like that where you feed a number of text strings to a function and it generates a bunch of randomly rotated grids that all fit together?

Link to comment
Share on other sites

So, you're saying maybe wrap each grouping of words/phrases in <g/> elements? Rotating and translating the <g/>'s would probably be much easier than trying to place text elements all over the place. I could wrap all those groups in a master <g/> and rotate that back and forth revealing each word or phrase one at a time. I'll give that a go, thanks.

Link to comment
Share on other sites

That camera demo I made can do that, but of course it would help if I had a published API so you could understand how to use it. The Rectangle and Point class in that demo aren't visual. They are math classes to calculate vectors and rectangle intersections. That's what I used for the grid interaction here.

See the Pen gLGYxx by osublake (@osublake) on CodePen


If I have time, I might try to set it up like you showed. I just made an update to that camera so you can see it with rotation.

See the Pen f4cfe6d1d7d317c9eca5e62216f1e0e7 by osublake (@osublake) on CodePen

Link to comment
Share on other sites

As for font-sizes, animation speed and even animate-in types of animations, those can be random. The idea is you would process what's in the textarea and if you like it, great. If you don't you can run it again to see what you get.

I thought I might look for a tilda ~ attached to a word to add a longer delay if a word needs emphasis.


This is one I created still using the viewBox thing. I sort of hacked the fade to be so fast you don't notice the weird delay. It shows the form field and the tilda.


See the Pen PbQNXz?editors=1010 by swampthang (@swampthang) on CodePen


Blake, I agree that I need to move away from animating the viewbox. The above was from before your last messages.

Link to comment
Share on other sites

Chronicling this as I go. Here's an image of a pattern for initially laying out groups of text. The idea is to rotate and/or position each group at the start and then as each word is revealed, do the zoom thing. When a rotated block is encountered, rotate the entire master group to make that group horizontal revealing each word of that group and so on.


To position a rotated block, I would first position it to the appropriate spot and then do a rotation. For example, the top-left point of the first rotated block in the pattern above would be positioned at the bottom right of the group that came before with the x value offset enough to scoot it over to the end of the last line in the previous group - the 'D' in the line - "LEFT ALIGNED". Then...
TweenMax.set('#group-1',{rotation: 90, transformOrigin: 'left top'})


After that is set, the top left of the 3rd group would be positioned at the new bottom right of the second group offsetting the y value to the top of the last word in the first line there and so-on.

Link to comment
Share on other sites

Yup. I made that demo real quick, and I didn't set it up for SVG, but most of it would work the same. I'll try to do that tonight. The code for the camera will be different, so I wouldn't worry about trying to figure it out. 


See if you can make that layout in SVG, and I'll try to adapt it to figure out the transformations.

Link to comment
Share on other sites

Awesome, Blake! Here's the layout. I included the textarea and dynamically building it. Hope that's not too much to have to deal with. Let me know if you want something simpler. I won't touch this one...


See the Pen PbymvR?editors=0010 by swampthang (@swampthang) on CodePen


The structure ends up being something like this after the init scripts run:

<svg id="stage" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0" y="0" width="800" height="600" viewBox="0 0 800 600">
    <g id="canvas" font-family="alpha_echoregular" fill="#ffffff" data-svg-origin="10 1.203125">
        <g font-size="39" id="group-0" class="text-group" data-svg-origin="0 -7" style="transform: matrix(1, 0, 0, 1, 10, 10);">
            <text class="text-0" font-size="39" fill="#0000ff">
                <tspan alignment-baseline="hanging">No </tspan>
                <tspan alignment-baseline="hanging">one</tspan>
            <text class="text-1" font-size="39" fill="#0000ff">
                <tspan alignment-baseline="hanging" y="39">knows</tspan>
        <g font-size="22" id="group-1" class="text-group" data-svg-origin="-0.734375 -4" style="transform-origin: 0px 0px 0px; transform: matrix(0, 1, -1, 0, 144.344, 90.0677);">
            <text class="text-2" font-size="22" fill="#4b0082" text-anchor="start">
                <tspan alignment-baseline="hanging">where </tspan>
                <tspan alignment-baseline="hanging">the</tspan>
            <text class="text-3" font-size="22" fill="#008000" text-anchor="start">
                <tspan alignment-baseline="hanging" y="22">wind</tspan>
            <text class="text-4" font-size="22" fill="#008000" text-anchor="start">
                <tspan alignment-baseline="hanging" y="44">blows</tspan>
        <g font-size="38" id="group-2" class="text-group" data-svg-origin="0 -7" style="transform: matrix(1, 0, 0, 1, 148.344, 206.927);">
            <text class="text-5" font-size="38" fill="#008000" text-anchor="start">
                <tspan alignment-baseline="hanging">Unless</tspan>
        <g font-size="30" id="group-3" class="text-group" data-svg-origin="-140.78125 -5.390625" style="transform: matrix(1, 0, 0, 1, 287.156, 242.927);">
            <text class="text-6" font-size="30" fill="#0000ff" text-anchor="end">
                <tspan alignment-baseline="hanging">you're</tspan>
            <text class="text-7" font-size="30" fill="#008000" text-anchor="end">
                <tspan alignment-baseline="hanging" y="30">standing</tspan>
        <g font-size="32" id="group-4" class="text-group" data-svg-origin="-1.078125 62.1875" style="transform-origin: 0px 0px 0px; transform: matrix(0, 1, -1, 0, 348.266, 300.802);">
            <text class="text-8" font-size="32" fill="#0000ff" text-anchor="start">
                <tspan alignment-baseline="hanging">in </tspan>
                <tspan alignment-baseline="hanging">a</tspan>
            <text class="text-9" font-size="32" fill="#4b0082" text-anchor="start">
                <tspan alignment-baseline="hanging" y="32">windstorm!</tspan>
Link to comment
Share on other sites

As an aside, since our next app is going to be a text animation app, and we want to add audio capabilities, I forked this to play around with it...

See the Pen sxdfe by GreenSock (@GreenSock) on CodePen


Hoping this might help someone...


We wanted an audio library that would create waveforms out of the box so started playing around with Wavesurfer.js.


I wanted to give the user the option to fade the audio at the end of an animation and assumed wavesurfer would have a method for fadeout but to my surprise, it doesn't! 


GSAP to the rescue! Was able to use TweenMax's awesome ability to tween any property of any object. Wavesurfer does have a ```setVolume``` method. So I just created an ```audioSettings``` object like:

audioSettings = {currentVolume: 1, fadeTime: 5}

Then used TweenMax's awesome onStart, onComplete and onUpdate methods like this...

var tl = new TimelineMax({delay:0.6, onStart: playAudio, onStartParams: [0], onComplete: fadeAudio})

Here are the 3 functions for Wavesurfer tied to GSAP...

function playAudio(location) {

function stopAudio() {

function fadeAudio() {
  TweenLite.to(audioSettings, audioSettings.fadeTime, {
    currentVolume: 0,
    ease: Sine.easeOut,
    onUpdate: function(){
    onComplete: function(){
      audioSettings.currentVolume = 1;

Worked like a charm! Here's the resulting codepen with an appropriate seasonal text. Be sure to click the ```DO PHRASES``` button to play the animation.


See the Pen qqQZqz by swampthang (@swampthang) on CodePen

Link to comment
Share on other sites

Nice Job  swampthing..


I do notice that in latest Firefox the text is not centered so the text is aligning right clipping half of the sentence out of your view box.


To have it centered properly in Firefox, you will have to add left: 0; to your h3. Firefox follows web standards so its best to define left property even if it is at 0 (zero)

#type-container h3 {

MS Edge doesn't animate or play any sound or animation.


As a rule of thumb when using position absolute you should always define top and left .. or a position offset default value to prevent the browser defined style sheet from not applying the proper position of your absolutely positioned element ;)

  • Like 1
Link to comment
Share on other sites

Here's the latest version and it's working (includes some audio as well) but, I couldn't figure another way (inside an SVG) to rotate text and reveal one word at a time other than to use a text element for each line and place tspan's inside each of those for each word. 


Thing is, I have to be able to provide a textarea for the user to enter their text for animation allowing them to use line breaks for new lines and double line-breaks for separating blocks. 


In building this out, it never occurred to me that I would be limited to only opacity and x/y or cx/cy animations for the tspans. Can't do scaleY, scaleX, etc. Would be awesome to be able to add other word-enter animations than just appearing or dropping in.


See the Pen ObdvLb?editors=1010 by swampthang (@swampthang) on CodePen


The reason I am trying to stay with this in an SVG is the resulting animations need to be exported to video files. Don't know of an efficient way to export a DOM element to an mp4 or other file. I've seen some dom2png node modules but they're all so crazy slow as to be impractical.


I can export PNG's of an SVG stepping through the animation pretty quickly and then use FFMPEG to build a movie file from them.

Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
  • Recently Browsing   0 members

    • No registered users viewing this page.
  • Create New...