Jump to content
Search Community

TweenMax.set drawSVG:0 in node.js not working

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 creating an app in a node.js environment and using a set of functions like the ones in the codepen.

 

The codepen works and everything works in the node.js app as well except this call in the hideStrokes function:

TweenMax.set(shapes, {drawSVG:0});

I see the svg container animate to the center of the screen but the lines in the svg appear as soon as I run the unHideSVG function. I can inspect the page using Chrome's dev tools and see that the drawSVG script was executed properly because I can see all the transforms, etc. 

 

Works in the codepen though. Anyone know of a reason why that particular call would fail in a node environment?

 

 

By the way, I am using the npm install version of gsap and it did install all of the files and plugins in the node_modules directory. 

 

In the js file where all the functions are running I require the module like:

var TweenMax = require("gsap");

Calls to drawSVG are not erroring so I assume it's finding it.

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

Link to comment
Share on other sites

What version of GSAP are you using? Your CodePen demo is using an old version.

 

In a pure node environment, loading TweenMax would throw an error, so I'm guessing there is window object when it's loaded. I would first try to animate the strokes using CSS just to see if the issue is related to GSAP or your environment. SVG is quirky across the board.

 

Here's a simple example you can test out.

See the Pen bGyoz by chriscoyier (@chriscoyier) on CodePen

  • Like 3
Link to comment
Share on other sites

Thanks, OSUBlake, for the reply and test svg. Your test animation worked just fine in the app. Also, I remember it working once before.

 

To answer your version question, here's from the package.json...

{
  "name": "gsap",
  "filename": "TweenMax.min.js",
  "version": "1.18.5",
  "description": "Think of GSAP as the Swiss Army Knife of animation...but better. It animates anything JavaScript can touch (CSS properties, canvas library objects, SVG, generic objects, whatever) and it solves countless browser inconsistencies, all with blazing speed (up",
  "homepage": "http://greensock.com/gsap/",
  "main": "./src/uncompressed/TweenMax.js",

However, I was wrong when I said I thought the drawSVG script was executed properly - it isn't. I added a console.log at the top of the set: method in DrawSVGPlugin.js and it didn't write anything to the console.

 

Is there something I need to do besides this...

var TweenMax = require("gsap");

to set this up?

 

At the top of the DrawSVGPlugin.js file I see this...

/*!
 * VERSION: 0.0.11
 * DATE: 2016-05-24
 * UPDATES AND DOCS AT: http://greensock.com
 *
 * @license Copyright (c) 2008-2016, GreenSock. All rights reserved.
 * DrawSVGPlugin is a Club GreenSock membership benefit; You must have a valid membership to use
 * this code without violating the terms of use. Visit http://greensock.com/club/ to sign up or get more details.
 * This work is subject to the software agreement that was issued with your membership.
 * 
 * @author: Jack Doyle, jack@greensock.com
 */
var _gsScope = (typeof(module) !== "undefined" && module.exports && typeof(global) !== "undefined") ? global : this || window; //helps ensure compatibility with AMD/RequireJS and CommonJS/Node
(_gsScope._gsQueue || (_gsScope._gsQueue = [])).push( function() {

At the top of the TweenMax.js file I see this...

/*!
 * VERSION: 1.18.5
 * DATE: 2016-05-24
 * UPDATES AND DOCS AT: http://greensock.com
 * 
 * Includes all of the following: TweenLite, TweenMax, TimelineLite, TimelineMax, EasePack, CSSPlugin, RoundPropsPlugin, BezierPlugin, AttrPlugin, DirectionalRotationPlugin
 *
 * @license Copyright (c) 2008-2016, GreenSock. All rights reserved.
 * This work is subject to the terms at http://greensock.com/standard-license or for
 * Club GreenSock members, the software agreement that was issued with your membership.
 * 
 * @author: Jack Doyle, jack@greensock.com
 **/
var _gsScope = (typeof(module) !== "undefined" && module.exports && typeof(global) !== "undefined") ? global : this || window; //helps ensure compatibility with AMD/RequireJS and CommonJS/Node
(_gsScope._gsQueue || (_gsScope._gsQueue = [])).push( function() {
As an aside, I'm wondering why there's a TweenMax-2.js file in the uncompressed folder.
Link to comment
Share on other sites

Yeah, you're using the latest version. There were changes made to improve module loading with that version. I thought Jack said he put all the globals on TweenMax, but I can't find in the code where he made those changes.

 

Just requiring gsap should make everything in the TweenMax file global. Assuming you've kept the folder structure the same, see if this works...

require("gsap");
var DrawSVGPlugin = require("gsap/plugins/drawsvgplugin"); 
  • Like 1
Link to comment
Share on other sites

Could this be a licensing issue? We do have a Business Green membership but it's under a different account name than mine. I've had this account (connected to my personal email) for some time now so prefer using it for this kind of communication.

 

Here's how I installed GSAP (maybe someone can see what I omitted or did wrong):

 

First, I ran...

npm install gsap

That created a folder structure that looks like this...

 

gsap-npm-folders.png

 

I had downloaded the club version of GSAP so unzipped it and diffed the 2. All I did was add the missing files to the node version. If there had been differences in the files that were in both folders, my diff app would have pointed them out. You can see the diff here...

 

gsap-npm-bg-diff.png

 

So, is there a place in the node files (or even the package.json file) where I need to add something for each of the Business Green plugins?

Link to comment
Share on other sites

Ahhhh, just saw your post above. Where exactly do I put that? 

 

Nevermind, I was able to figure it out. Thanks for the tip! Got it to work. I had to use the node path though as you'll see below.

 

I guess this is something that I'll have to do for all the Business Green plugins that I wish to use. Here's how I have it set up for anyone else who needs help with this. Might not be the perfect way, but it works.

 

I had created a custom module called svg-animate and the package.json file has index.js as the main script to run. 

{
  "name": "svg-animate",
  "version": "1.0.0",
  "description": "A set of animation functions for svg files using Greensock's GSAP utilities",
  "main": "index.js",

I had set up the index.js file in that module to require gsap and thought, since everything else seemed to be working, that the plugins were included as well. Since finding out different, I added the var DrawSVGPlugin line just below where I added the TweenMax require statement...

var TweenMax = require("gsap");
var DrawSVGPlugin = require("gsap/src/uncompressed/plugins/drawsvgplugin");

Here's a SS of my editor showing the file structure and where I've added this to the index.js file..

 

drawsvg-added.png

Link to comment
Share on other sites

Oops. I just assumed you manually put the DrawSVG file in your node modules folder since it seemed like you were on the right track.

 

As you've noticed, paid plugins can only be downloaded from your user account. If you're going to be doing a lot of prototyping with them, or have other team members that will need access to them, you might want to look into creating a private package/repo for them. That's what I do. Whenever a new version comes out, I just update my package.

 

Just out of curiosity, what are you working on? I haven't had a reason to build a desktop app for some time now, but Electron definitely looks appealing if the need ever arises.

Link to comment
Share on other sites

Hey Swampthang,

 

Glad you got things working and thanks for sharing all the details along the way.

Just so you know, the bonus files you download from your account will work anywhere. They don't know who you are or have any idea where they are living. There's no "phoning home" as they say, and they also won't make your app blow up after a certain date. Some people worry about that ;)

 

Little shout-out to Blake for jumping to the rescue.

 

-

  • Like 2
Link to comment
Share on other sites

You bet, Carl, thanks for clarifying - and yea, thanks, Blake! 

 

As to what I'm building, it's an app that folks will be able to install where they can pull in any number of SVG's, enter text and animate them along the timeline using some very simple assembly tools. I've got the basic idea working and there's a lot more to go - miles to go before I sleep ;-)

 

I'm stuck right now at the most important feature and that's finding a way to export the animations for use in video editing (mp4's) or on the web via animated GIFs. Striking out left and right. I would invite anyone who thinks they might have a solution to let me know because I'd be happy to subcontract this part of the project out or pay someone hourly to help me get set up and headed in the right direction.

 

And, yea, Electron is an awesome build tool. The github folks are developing it and it's getting more powerful every day. In fact, there are several apps on the market right now that were built in it. It's all HTML5, css and js. Pretty awesome for someone with front dev skills already!

Link to comment
Share on other sites

Sorry about the TweenMax-2.js file. My fault - ignore that. I'll remove it in the next push. I was testing something and left it in there accidentally. Also, 1.18.5 did indeed improve the global exports from TweenMax, but there was an issue that could cause some of the globals not to get exported properly which is fixed in the upcoming 1.18.6 release which you can preview at https://s3-us-west-2.amazonaws.com/s.cdpn.io/16327/TweenMax-latest-beta.js (uncompressed). 

 

Glad to hear you got things working. 

 

That Electron thing sounds cool, although from another thread it appeared as though it might have some bugs, particularly with transform matrices (swapping commas in for periods in the strings). Are you wrestling with that at all? 

Link to comment
Share on other sites

I use a couple Electron apps like Boxy SVG and VS Code. I've been waiting months for Microsoft to open source their Monaco editor, which is what powers VS Code. I just checked, and they've finally released it. I thought about using it to make a GSAP playground because I really hate using CodePen's editor. I was thinking about making it a web app, but you've piqued my interest into maybe using it for an Electron app.

 

To create a movie from an SVG animation, just convert each animation frame to an image. All you have to do is create a Blob from your SVG. From there you can create a data URL, which can be used as the source for an image. Since you don't have to deal with stupid browsers like IE and Safari, this should be pretty simple.

 

Once you've created the images, all you have to do use a GIF-encoder to create the movie. Encoding the movie can take a while, so try to find one that places an emphasis on performance. I'm not that familiar with any particular one, so I can't make any recommendations.

 

I just made a quick demo using DrawSVG. Is that what you're going after?

See the Pen 336e66d9806eb5c10158083ee27cacd8?editors=0010 by osublake (@osublake) on CodePen

  • Like 4
Link to comment
Share on other sites

All i have to do now is figure out how to capture all the animations including x/y Tweens and Bounce, etc. That's probably why most folks I've seen have opted to try screen capture. 

 

I'm wondering if I could use a simple screen capture utility and do the same you were doing, pause and then advance 1 "frame" at a time but do that for a master timeline. These animations could get a little hairy. I think I'm gonna give that a go.

Link to comment
Share on other sites

Hey, Blake. I've tried to avoid using canvas and there are very few good screenshot utilities for Electron that don't require canvas. I really like your approach best but have a glitch in my understanding of how to apply it to a complex animation.

 

I have to admit that I haven't done a lot of work in converting Blob's and creating data URL's. So, I'm going back to school on them right now. Before I waste a lot of time heading down that road, do you think it's practical or possible to create data URLs of each frame of a complex master timeline where elements are being positioned at various x/y points as well as being drawn in, etc? I keep thinking there's got to be a way to do this and preserve transparency in the resulting animated gif or MP4.

 

What I know...

I'll have a set container size like 1024 x 768 or 1440 x 900, etc. All animations will take place inside that container. I'll only use SVGs and text. Want to be able to use the splitText plugin as well as drawSVG. 

 

I keep thinking I'm so close to something that could be so much better than screen capture because I bet this could be resolution independent. Might even be able to "export" animated GIFs and MP4s at 2X resolution, etc.

 

While I know this isn't something that is GSAP-specific, I think there's something really useful for GSAP users that could result from this and create a whole new use for these animations. Your thoughts?

Link to comment
Share on other sites

The reason taking a screen recording is so difficult is that it's a huge security risk. It's to prevent attackers from capturing content like your passwords or bank account info.

 

To get around those security restrictions, you can draw the content on a canvas or embed it inside an SVG foreignObject. The reason you never see anybody use the foreignObject approach is because it doesn't work in IE. Since you don't have to worry about IE, you can use SVG and skip using the canvas.

 

The Blob to data URL conversion is only done to clear the SVG of it's origin. This will allow you to get around any cross-origin restrictions. I haven't tested this with SVGs, but I know that with regular images you can clear the origin by adding it to localStorage, and then retrieving it.

 

When you convert an SVG to an image, it doesn't lose the SVG data, so it will still be resolution independent. However, the foreignObject doesn't use the same viewBox, so it's not going scale the same way. It might be easier to just use SVG text instead of HTML. This will make converting everything from a master timeline much easier as everything will be rendered using the same viewBox.

 

Here's another version of my demo. I set the SVG to 1x1 so that you can see that the images are resolution independent. The foreignObject text has it's own set of coordinates, so it's not synced with the SVG's coordinates. And notice how the foreignObject isn't being displayed correctly in the first frame. Not sure what's up with that. Probably some weird styling going on during initialization.

 

See the Pen 4a95dbb9e7272430b73d8d77ac310ba5 by osublake (@osublake) on CodePen

  • Like 2
Link to comment
Share on other sites

Thanks for the reply, Blake. Yea, nice that everything is one browser and controlled version. Cross origin policy restrictions aren't an issue with Electron either. I'll play with that option.

 

I notice that you're embedding everything inside the svg tag. Is that something that can't be avoided with your method? I need to be able to animate entire div's containing either text or svg's inside a containers coords. So, for example, a structure like this where the #stage element might be 1440 x 900 might have 2 different elements like...

 

<div id="stage">

  <!-- Will animate this using scrambleText, etc -->
  <div id="text1" class="text-container">
    Some text here
  </div>

  <div id="svg1" class="svg-container">
    <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 170 170" class="svg inlined-svg" role="img" id="svg-0">
      <g id="Background"/>
      <g id="Circle">
        <path fill="#FFFFFF" d="M85 170c-46.87 0-85-38.13-85-85S38.13 0 85 0s85 38.13 85 85S131.87 170 85 170z"/>
      </g>
      <g id="TEXT"/>
      <g id="Icons">
        <line fill="none" stroke="#282D33" stroke-width="4" x1="32.43" y1="126.4" x2="130.43" y2="126.4"/>
        <path fill="none" stroke="#282D33" stroke-width="4" d="M87.09 121.73v-4.61c0-1.27 1.03-2.31 2.31-2.31h33.45c1.27 0 2.31 1.03 2.31 2.31v4.61"/>
        <path fill="none" stroke="#282D33" stroke-width="4" d="M122.45 109.05c0-1.27-1.03-2.31-2.31-2.31H92.08c-1.27 0-2.31 1.03-2.31 2.31v3.46c0 1.27 1.03 2.31 2.31 2.31h28.06c1.27 0 2.31-1.03 2.31-2.31V109.05z"/>
        <path fill="none" stroke="#282D33" stroke-width="4" d="M122.11 64.67c0.79 0.79 0.79 2.08 0 2.87l-18.88 18.88c-0.79 0.79-2.08 0.79-2.87 0l-4.54-4.54c-0.79-0.79-0.79-2.08 0-2.87l18.88-18.88c0.79-0.79 2.08-0.79 2.87 0L122.11 64.67z"/>
        <path fill="none" stroke="#282D33" stroke-width="4" d="M97.49 40.06c0.79 0.79 0.79 2.08 0 2.87L78.61 61.8c-0.79 0.79-2.08 0.79-2.87 0l-4.54-4.54c-0.79-0.79-0.79-2.08 0-2.87l18.88-18.88c0.79-0.79 2.08-0.79 2.87 0L97.49 40.06z"/>
        <line fill="none" stroke="#282D33" stroke-width="4" x1="98.02" y1="77.55" x2="80.07" y2="59.61"/>
        <line fill="none" stroke="#282D33" stroke-width="4" x1="113.24" y1="62.33" x2="95.3" y2="44.39"/>
        <rect x="42.45" y="83.84" transform="matrix(-0.7071 0.7071 -0.7071 -0.7071 181.2005 100.7024)" fill="none" stroke="#282D33" stroke-width="4" width="54.59" height="8.07"/>
        <rect x="43.27" y="104.12" transform="matrix(0.7071 -0.7071 0.7071 0.7071 -63.9865 65.8295)" fill="none" stroke="#282D33" stroke-width="4" width="8.41" height="12.07"/>
        <line fill="none" stroke="#282D33" stroke-width="4" x1="83.88" y1="79.18" x2="78.44" y2="73.74"/>
        <line fill="none" stroke="#282D33" stroke-width="4" x1="78.17" y1="84.89" x2="72.73" y2="79.45"/>
        <line fill="none" stroke="#282D33" stroke-width="4" x1="100.11" y1="65.05" x2="92.57" y2="57.51"/>
        <line fill="none" stroke="#282D33" stroke-width="4" x1="122.07" y1="90.9" x2="130.82" y2="90.9"/>
        <line fill="none" stroke="#282D33" stroke-width="4" x1="120.75" y1="84.37" x2="128.13" y2="79.68"/>
        <line fill="none" stroke="#282D33" stroke-width="4" x1="120.75" y1="97.43" x2="128.13" y2="102.12"/>
        <line fill="none" stroke="#282D33" stroke-width="4" x1="91.5" y1="90.9" x2="82.75" y2="90.9"/>
        <line fill="none" stroke="#282D33" stroke-width="4" x1="92.82" y1="97.43" x2="85.44" y2="102.12"/>
      </g>
    </svg>
  </div>
</div>

The text might be animating in from left to right and then drawing while the text comes in from right to left and scrambling, etc. 

Link to comment
Share on other sites

The only other way is to draw it on a canvas. But like I said, it has it's own coordinate system, so it can move relative to the stage. You can also nest SVGs inside of each other.

 

Here's another version. The stage is the green outline, and the SVG is still that 1x1 red outline. 

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

 

The image is going to use the SVG's viewBox, so you should probably set your SVG's width and height to 100% so it's fills up the entire stage.

  • Like 1
Link to comment
Share on other sites

So, here's the progress...

 

I have Blake's

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

working in my application. Electron has full access to the filesystem and to anything that can be done in node.js. 

 

In Blake's 

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

 the section that creates and appends the blob image to an element is...

var xml  = new XMLSerializer().serializeToString(svg);  
var blob = new Blob([xml], { type: "image/svg+xml" });  
var img  = new Image();


img.crossOrigin = "Anonymous";  
img.onload = function() {     
  list.appendChild(this);
};


img.src = URL.createObjectURL(blob);

and it creates multiple img elements that look like this...

<section>
  <img crossorigin="Anonymous" src="blob:http%3A//s.codepen.io/e8ccd85d-cd49-4c20-9445-e841a9b96142">
  <img crossorigin="Anonymous" src="blob:http%3A//s.codepen.io/2c1e1cd4-232d-44b6-a919-f7637a1cce39">
  <img crossorigin="Anonymous" src="blob:http%3A//s.codepen.io/3eae7d75-27ea-44e5-bc45-b069e71e6da4">
  <img crossorigin="Anonymous" src="blob:http%3A//s.codepen.io/e48c7b71-5127-4691-af12-19cbbb45cbe5">
  <img crossorigin="Anonymous" src="blob:http%3A//s.codepen.io/dc3e933e-6fd8-4f24-a4d3-fb8583b5c517">
</section>

See next post...

Link to comment
Share on other sites

Figured out how to store the svg file. Was overthinking this. All I had to do was write the xml variable created here..

var xml  = new XMLSerializer().serializeToString(svg);

like this...

fs.writeFileSync('./tmp/img'+current+'.svg', xml);

FFMPEG doesn't support export from SVG so had to convert the sequence of SVGs to an exact sequence of PNGs. I found this which does the job beautifully. 

 

https://www.npmjs.com/package/blender

 

It takes an input directory and, using a JSON config file, it converts to PNGs and saves them to whatever directory you give as an output.

 

However, before I was able to create the videos I had to know that all the PNGs were created so I had to set up a promise-based series of functions and already knew about this really cool "promise-style async sequence flow-control" npm package:

 

https://github.com/getify/asynquence

 

 With that working, I turned to FFMPEG and found that I needed to name the sequence of images with leading zeros - like img00001.png, img00002.png, etc because the way FFMPEG works out of the box is like this...

ffmpeg().input("./tmp/pngs/desktop/img%04d.png") 

The %04d part tells FFMPEG that we have a sequence of images named img{number}.png and that there are 4 leading zeros. 

 

Got the video export working now so almost there!

 

The next big hurdle is figuring out how to package (dynamically link) FFMPEG in the packaged Electron apps.

Link to comment
Share on other sites

  • 1 month later...
  • 2 months later...

The reason taking a screen recording is so difficult is that it's a huge security risk. It's to prevent attackers from capturing content like your passwords or bank account info.

 

To get around those security restrictions, you can draw the content on a canvas or embed it inside an SVG foreignObject. The reason you never see anybody use the foreignObject approach is because it doesn't work in IE. Since you don't have to worry about IE, you can use SVG and skip using the canvas.

 

The Blob to data URL conversion is only done to clear the SVG of it's origin. This will allow you to get around any cross-origin restrictions. I haven't tested this with SVGs, but I know that with regular images you can clear the origin by adding it to localStorage, and then retrieving it.

 

When you convert an SVG to an image, it doesn't lose the SVG data, so it will still be resolution independent. However, the foreignObject doesn't use the same viewBox, so it's not going scale the same way. It might be easier to just use SVG text instead of HTML. This will make converting everything from a master timeline much easier as everything will be rendered using the same viewBox.

 

Here's another version of my demo. I set the SVG to 1x1 so that you can see that the images are resolution independent. The foreignObject text has it's own set of coordinates, so it's not synced with the SVG's coordinates. And notice how the foreignObject isn't being displayed correctly in the first frame. Not sure what's up with that. Probably some weird styling going on during initialization.

 

See the Pen 4a95dbb9e7272430b73d8d77ac310ba5 by osublake (@osublake) on CodePen

 

I'm working on a new project that involves real hands interacting with other items and this too will be done using Electron so it's only gonna involve the Chromium browser. I borrowed Jamie's png sequence code to play with this found in Jack's post here:

http://greensock.com/forums/topic/9310-animate-a-large-sequence-of-pngs/?p=37611

The png sequence is dynamically loaded into a foreignObject inside the master-svg. Here's the pen:

 

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

 

I'm trying to utilize Blake's code here:

 

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

 

to loop through the animation and capture everything to a png sequence that includes the gp element that gets pushed off the stage by the hand. 

 

I noticed in debug mode that the foreignObject is being moved by Chrome out of the svg and into the main body of the html so the foreignObject looks like it's empty.

 

I wondered if it was because I was adding the images using jquery and not document.createElementNS() so I tried that and it did the same thing. I also created another version of the pen with all the images already in the foreignObject:

 

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

 

But no images are being created. Anyone know why the foreignObject is being moved out of the SVG?

Link to comment
Share on other sites

Ok, I'm an idiot. I misspelled foreignObject. Had spelled it foreignOject (without the 'b'). The foreignObject isn't being moved out. So, the only thing left to resolve is why the images disappear when trying to copy frames to a png sequence. I fixed the spelling in the codepen and revised a couple of scripts to use javascript instead of jQuery. 

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

Still a mystery. I'll eventually solve this and post the solution here for anyone else who runs into this issue.

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...