Jump to content
GreenSock

Hippiesvin

Drawing with TweenMax [SOLVED]

Recommended Posts

I have been trying to find examples on how to draw shapes and lines using TweenMax, and haven't been able to find any.

So I thought I might not be the only one needing some expert advice on this subject.

 

I found my own way drawing a simple retangle, where each sideline get's animated using the onUpdate:Function, so you actually see the retangle being drawed.

My problem is though, that my method seems to be a little unstabil especially when I speed up the drawing.

 

Question:

Is there an easier/better way to draw lines and shapes with TweenMax than my example below?

 

// CODE ====================

import fl.motion.easing.*;

import flash.display.*;

import gs.TweenMax;

import gs.easing.*;

 

var line:Shape = new Shape();

line.graphics.lineStyle(10, 0xFFD700, 1, false, LineScaleMode.VERTICAL, CapsStyle.NONE, JointStyle.MITER, 10);

 

this.addChild(line); line.x = 50; line.y = 50;

 

var Drawer:Sprite = new Sprite(); this.addChild(Drawer);

 

TweenMax.to(Drawer, 0.1, { delay: 0, x:200, ease:Quadratic.easeOut, onUpdate:drawLine });

TweenMax.to(Drawer, 0.1, { delay: 0.1, y:100, ease:Quadratic.easeOut, onUpdate:drawLine });

TweenMax.to(Drawer, 0.1, { delay: 0.2, x:0, ease:Quadratic.easeOut, onUpdate:drawLine });

TweenMax.to(Drawer, 0.1, { delay: 0.3, y:0, ease:Quadratic.easeOut, onUpdate:drawLine });

 

function drawLine() {

var X:int = Drawer.x;

var Y:int = Drawer.y;

line.graphics.lineTo(X, Y);

}

// END CODE ====================

Link to comment
Share on other sites

Sure, that should work. Or you could use a TweenGroup which could come in handy if you want to skip ahead or reverse or something...

 

import fl.motion.easing.*;
import flash.display.*;
import gs.*;

var line:Shape = new Shape();
line.graphics.lineStyle(10, 0xFFD700, 1, false, LineScaleMode.VERTICAL, CapsStyle.NONE, JointStyle.MITER, 10);

this.addChild(line); 
line.x = 50; 
line.y = 50;

var drawer:Sprite = new Sprite(); 
this.addChild(drawer);

var sequence:TweenGroup = new TweenGroup([{target:drawer, time:0.1, x:200, ease:Quadratic.easeOut, onUpdate:drawLine},
									  {target:drawer, time:0.1, y:100, ease:Quadratic.easeOut, onUpdate:drawLine},
									  {target:drawer, time:0.1, x:0, ease:Quadratic.easeOut, onUpdate:drawLine},
									  {target:drawer, time:0.1, y:0, ease:Quadratic.easeOut, onUpdate:drawLine}], TweenLite, TweenGroup.ALIGN_SEQUENCE);

function drawLine():void {
line.graphics.lineTo(int(drawer.x), int(drawer.y));
}

 

If you want your easing to apply to the whole group instead of each individual line, you could even set them up in a group each with an ease of Linear.easeNone and then tween the "progress" property of the group and use an ease in that tween. Lots of possibilities. :)

Link to comment
Share on other sites

Hi GreenSock - and thank you for your quick reply, you are really doing a great job :)

 

Previewing after copying and pasting your code into a new flash project throws 1 error back from the compiler.

Compiling in strict mode throws 3 errors.

 

The errors are located in the var sequence:TweenGroup......

 

Any idea why?

I've checked for missing commas and so.

 

/Hippiesvin

Link to comment
Share on other sites

Hi it's me again :)

 

doing like this:

 

var sequence:TweenGroup = new TweenGroup();

sequence = [

{target:drawer, time:0.1, x:200, ease:Quadratic.easeOut, onUpdate:drawLine},

{target:drawer, time:0.1, y:100, ease:Quadratic.easeOut, onUpdate:drawLine},

{target:drawer, time:0.1, x:0, ease:Quadratic.easeOut, onUpdate:drawLine},

{target:drawer, time:0.1, y:0, ease:Quadratic.easeOut, onUpdate:drawLine}], TweenLite, TweenGroup.ALIGN_SEQUENCE);

 

Brings down the errors to 1, strictmode or not.

the problem is in this line:

{target:drawer, time:0.1, y:0, ease:Quadratic.easeOut, onUpdate:drawLine}], TweenLite, TweenGroup.ALIGN_SEQUENCE);

Link to comment
Share on other sites

Okay I was using a downloaded version of TweenMax (2.34) from September that didn't contain the TweenGroup.as document, so that explains a lot, so I've got your code up and running BUT...

it's still not drawing a perfect rectangle exactly, just as my own piece of code (referred to as "a little unstabile" in my first post). See attached .jpg

 

It dosn't matter if I set time to 1 sec or remove all easing, it's like the next tween in the sequence is starting a little before the previous one has finished, causing the lines to be a little shattered? Am I right?

 

Am I pushing my luck trying to animate drawing with TweenMax?

 

thanks for taking time :)

Link to comment
Share on other sites

  • 7 months later...

Unfortunately this example would not work with v11 since Tweengroup is now TimelineLite.

 

Based on v11 I would like to see an example function drawShape(xypoints:Array) where a shape is drawn with a sequence of straight lines and drawBezier(xyPoints:Array) with bezier-lines.

 

Unfortunately I have not been able to do it

Link to comment
Share on other sites

Sure, v11 opens up lots of other possibilities, and it's just as easy to accomplish this task with TimelineLite as it was with TweenGroup:

 

import flash.display.*;
import gs.*;
import gs.easing.*;

var line:Shape = new Shape();
line.graphics.lineStyle(10, 0xFFD700, 1, false, LineScaleMode.VERTICAL, CapsStyle.NONE, JointStyle.MITER, 10);

this.addChild(line);
line.x = 50;
line.y = 50;

var drawer:Sprite = new Sprite();
this.addChild(drawer);

var sequence:TimelineLite = new TimelineLite({tweens:[
							[drawer, 0.1, {x:200, ease:Quad.easeOut, onUpdate:drawLine}],
							[drawer, 0.1, {y:100, ease:Quad.easeOut, onUpdate:drawLine}],
							[drawer, 0.1, {x:0, ease:Quad.easeOut, onUpdate:drawLine}],
							[drawer, 0.1, {y:0, ease:Quad.easeOut, onUpdate:drawLine}]], align:TimelineLite.ALIGN_SEQUENCE}); 

function drawLine():void {
  line.graphics.lineTo(drawer.x, drawer.y);
}

Link to comment
Share on other sites

What about a dynamic function? I would like to pass an array/object of xy values to a tweening function to draw continuous lines (e.g. to build a square or more advanced shapes).

 

I'd even imagine this as a Plugin? Drawing beziers based on a object as well. Or is it already implemented and I don't get it?

Link to comment
Share on other sites

@greensocks

I am trying to achieve an tweened animation like this with a array of points

 

post-3238-133151999696_thumb.jpg

 

 

Any hint?

Link to comment
Share on other sites

Here's something I whipped together. You could certainly modularize it more, put it into classes, etc., but hopefully it gives you the general idea. You could just copy/paste this into a blank FLA and publish it to see (make sure you've got the latest v11 "gs" directory with the FLA though):

 

import flash.display.*;
import flash.geom.*;
import gs.*;
import gs.easing.*;

var line:Shape = new Shape();
this.addChild(line);

var myPath:TimelineLite = buildPathTimeline([new Point(40, 200), 
										 new Point(120, 130), 
										 new Point(150, 220),
										 new Point(260, 70),
										 new Point(290, 120)], line.graphics);

TweenLite.to(myPath, 5, {progress:1}); //simply tween the progress property from 0 to 1 to draw the line.

function buildPathTimeline($points:Array, $graphics:Graphics):TimelineLite {
var timeline:TimelineLite = new TimelineLite({paused:true});
var p:Point //point
var pPrev:Point = $points[0];
var prevPoints:Array = []; //keep track of the points that come before each point in the sequence
var dx:Number; //distance on the x-axis
var dy:Number; //distance on the y-axis
var d:Number; //distance
var pen:Object; //stores information about the coordinates and previous points for each one in the Array
for (var i:int = 1; i 		p = $points[i];
	pPrev = $points[i - 1];
	prevPoints.push(pPrev);
	pen = {x:pPrev.x, y:pPrev.y, prevPoints:prevPoints.slice()};
	dx = p.x - pPrev.x;
	dy = p.y - pPrev.y;
	d = Math.sqrt(dx * dx + dy * dy);
	timeline.append(new TweenLite(pen, d, {x:p.x, y:p.y, ease:Linear.easeNone, onUpdate:updateLine, onUpdateParams:[$graphics, pen]}));
}
return timeline;
}

function updateLine($graphics:Graphics, $pen:Object):void {
$graphics.clear();
$graphics.lineStyle(10, 0xFFD700, 1, false, LineScaleMode.VERTICAL, CapsStyle.ROUND, JointStyle.ROUND, 10);
var points:Array = $pen.prevPoints;
$graphics.moveTo(points[0].x, points[0].y);
var l:uint = points.length;
for (var i:int = 1; i 		$graphics.lineTo(points[i].x, points[i].y);
}
$graphics.lineTo($pen.x, $pen.y);
}

Link to comment
Share on other sites

Thanks alot, I definately give it a try :D

Link to comment
Share on other sites

  • 6 months later...

I wanted to animate the line drawing between several points all at the same time. And, with a few tweaks of your above sample, I got it working :D I only had to comment out the graphics clear and change "progress" to the "currentProgress" property. Keeping the graphics clear would only draw one line instead of all the 40 points I wanted to connect at once.

 

I've included my solution if helps anyone else. In the below code, you can use drawConnection() in a loop to connect all the sprite dots you placed on the stage to whatever pattern you wish -- ie a spider web.

 

       private function buildPathTimeline($points:Array, $graphics:Graphics):TimelineLite 
	{
		   var timeline:TimelineLite = new TimelineLite();
		   var p:Point //point
		   var pPrev:Point = $points[0];
		   var prevPoints:Array = []; //keep track of the points that come before each point in the sequence
		   var dx:Number; //distance on the x-axis
		   var dy:Number; //distance on the y-axis
		   var d:Number; //distance
		   var pen:Object; //stores information about the coordinates and previous points for each one in the Array
		   for (var i:int = 1; i < $points.length; i++) {
		      p = $points[i];
		      pPrev = $points[i - 1];
		      prevPoints.push(pPrev);
		      pen = {x:pPrev.x, y:pPrev.y, prevPoints:prevPoints.slice()};
		      dx = p.x - pPrev.x;
		      dy = p.y - pPrev.y;
		      d = Math.sqrt(dx * dx + dy * dy);
		      timeline.append(new TweenLite(pen, d, {x:p.x, y:p.y, ease:Linear.easeNone, onUpdate:updateLine, onUpdateParams:[$graphics, pen]}));
		   }
		   return timeline;
		}

	private function updateLine($graphics:Graphics, $pen:Object):void 
	{
		  // $graphics.clear();
		   $graphics.lineStyle(5, 0xFFD700, 1, false, LineScaleMode.VERTICAL, CapsStyle.ROUND, JointStyle.ROUND, 10);
		   var points:Array = $pen.prevPoints;
		   $graphics.moveTo(points[0].x, points[0].y);
		   var l:uint = points.length;
		   for (var i:int = 1; i < l; i++) {
		      $graphics.lineTo(points[i].x, points[i].y);
		   }
		   $graphics.lineTo($pen.x, $pen.y);
		}

	private function drawConnection( fromDot:Sprite, toDot:Sprite):void
	{
		var point_from:Point = new point(fromDot.x, fromDot.y);
		var point_to:Point = new point(toDot.x, toDot.y);

		var myPath:TimelineLite = buildPathTimeline([ point_from, point_to], this.graphics);

		TweenLite.to(myPath, 0.5, {currentProgress:1}); //simply tween the progress property from 0 to 1 to draw the line.

	}

Link to comment
Share on other sites

  • 2 months later...
Here's something I whipped together. You could certainly modularize it more, put it into classes, etc., but hopefully it gives you the general idea. You could just copy/paste this into a blank FLA and publish it to see (make sure you've got the latest v11 "gs" directory with the FLA though):

 

i've made a class out of your example. maybe you could create a plugin out of it if you have some spare time? i think it could be a nice feature.

 

create an instance:

 

var points:Array = new Array(new Point(0, 0), new Point(-150, -150), new Point(-150, 50), new Point(20, 20));    
            
var style:GraphicsStroke = new GraphicsStroke(10, false, LineScaleMode.NORMAL,CapsStyle.NONE, JointStyle.MITER);            
style.fill = new GraphicsSolidFill(0x000000, 1);
            
var stroke:ProgressiveStroke = new ProgressiveStroke(points, 6, style, Quint.easeInOut);
stroke.play();
            
addChild(stroke);
 

 

and the class:

 

////////////////////////////////////////////////////////////////////////////////
// Copyright 2010.  
// All rights reserved. 
////////////////////////////////////////////////////////////////////////////////

package ch.insofern.graphics
{
    
    import __AS3__.vec.Vector;
    
    import com.greensock.TimelineLite;
    import com.greensock.TweenLite;
    import com.greensock.easing.Linear;
    
    import flash.display.Graphics;
    import flash.display.GraphicsSolidFill;
    import flash.display.GraphicsStroke;
    import flash.display.IGraphicsData;
    import flash.display.Shape;
    import flash.events.Event;
    import flash.geom.Point;
    
    [Event(name="complete", type="flash.events.Event")]
    
    /**
     * Class description.
     * @author negro
     */
    public class ProgressiveStroke extends Shape
    {
        /**
         * Creates an animated line.
         * @param points
         * @param duration
         * @param style
         * @param ease
         */
        public function ProgressiveStroke(points:Array, duration:Number = 3, style:GraphicsStroke = null, ease:Function = null)
        {
            // set a default ease
            if (ease == null)
            {
                ease = Linear.easeOut;
            }
            
            // set a default style
            if (style == null)
            {
                style = new GraphicsStroke(1);
                style.fill = new GraphicsSolidFill(0x000000);
            }
            
            // add the style to the graphics data
            _style = new Vector.<IGraphicsData>();
            _style.push(style);
            
            // create a timeline that contains a tween for every line
            var timeline:TimelineLite = new TimelineLite({paused: true});
            var point:Point;
            var pointPrevious:Point = points[0];
            var passedPoints:Array = [];
            var distance:Number;
            var pen:Object;
            
            for (var i:int = 1; i < points.length; i++)
            {
                point = points[i];
                pointPrevious = points[i - 1];
                passedPoints.push(pointPrevious);
                pen = {x: pointPrevious.x, y: pointPrevious.y, passedPoints: passedPoints.slice()};
                distance = Point.distance(point, pointPrevious);
                timeline.append(new TweenLite(pen, distance, {x: point.x, y: point.y, ease: Linear.easeNone, onUpdate: update, onUpdateParams: [pen]}));
            }
            
            // tween the timeline progress, this actually creates the animation
            _stroke = new TweenLite(timeline, duration, {currentProgress: 1, ease: ease, paused: true, onComplete: complete});
        }
        
        /**
         * Play the drawing.
         */
        public function pause():void
        {
            _stroke.pause();
        }
        
        /**
         * Pause the drawing.
         */
        public function play():void
        {
            _stroke.play();
        }
        
        /**
         * Dispatch complete event when the stroke 
         * drawing is complete.
         */
        private function complete():void
        {
            dispatchEvent(new Event(Event.COMPLETE));
        }
        
        /**
         * Update the drawing od the stroke.
         * @param pen
         */
        private function update(pen:Object):void
        {
            // clear the old lines and redraw everything
            var graph:Graphics = graphics;
            graph.clear();
            graph.drawGraphicsData(_style);
            
            // draw the lines for all points that were already passed
            var passedPoints:Array = pen.passedPoints;
            graph.moveTo(passedPoints[0].x, passedPoints[0].y);
            
            var l:uint = passedPoints.length;
            
            for (var i:int = 1; i < l; i++)
            {
                graph.lineTo(passedPoints[i].x, passedPoints[i].y);
            }
            
            // draw the line for the current point
            graph.lineTo(pen.x, pen.y);
        }

        //--------------------------------------
        // Properties 
        //--------------------------------------
        
        private var _stroke:TweenLite;
        
        private var _style:Vector.<IGraphicsData>;
    }
}
 

Link to comment
Share on other sites

Good idea to add a line drawing plugin to the API, but please add a dashed/dotted property to the class as well.

 

I am having problems adding an ease property, your example returns

1120: Access of undefined property Quint.

Link to comment
Share on other sites

Ease problem solved, forgot to import easing classes!

Link to comment
Share on other sites

  • 1 month later...

Hi,

 

I would like to tween shapes and copied and pasted the code from greensock's last post. Unfortunately it gives the following error.

 

Thanks for your help.

 

Frank

 

ReferenceError: Error #1069: Property progress not found on com.greensock.TimelineLite and there is no default value.

at com.greensock::TweenLite/com.greensock:TweenLite::init()[...\com\greensock\TweenLite.as:372]

at com.greensock::TweenLite/renderTime()[...\com\greensock\TweenLite.as:446]

at com.greensock.core::SimpleTimeline/renderTime()[...\com\greensock\core\SimpleTimeline.as:77]

at com.greensock::TweenLite$/com.greensock:TweenLite::updateAll()[...\com\greensock\TweenLite.as:629]

Link to comment
Share on other sites

That code was for a prerelease version of TimelineLite from quite a long time ago - before officially launching v11, I changed "progress" to "currentProgress" to distinguish it from TimelineMax's "totalProgress" property which describes the overall progress including repeats. Anyway, change "progress" to "currentProgress" in your code.

 

Also, you might want to look into the new (beta) MotionPath stuff that I've tucked into the com.greensock.motionPaths package. There's a one for circle, rectangle, and line (which accommodates multiple points and animate an object along it). So you could tween a simple Point object along the path and use an onUpdate to draw it.

Link to comment
Share on other sites

Thanks very much for the quick reply, it works and looks great.

 

What I would like to achieve but haven't been able to is tween line X into line Y, so tweening from one array of points to another. I don't know if this is possible?

Link to comment
Share on other sites

Sure, you can tween the Point object coordinates themselves in a LinePath2D if you want. It obviously increases the CPU load, but it's doable. Just set the autoUpdatePoints property to true and then tween away to your heart's content. You could not, however, change the number of Points in the line with that approach.

Link to comment
Share on other sites

Thanks! I plotted the lines using LinePath2D, but couldn't figure out how to tween the points.

 

It would be great if you could provide an example, or a line of code for the tween to get me started...

 

Here's an example of the effect I'm trying to achieve (but this example is with shapes): http://www.motiondraw.com/blog/?p=51

Link to comment
Share on other sites

Sure, I've attached an example. I also had to make one small tweak to the LinePath2D class, so make sure you use the attached one.

 

Is this what you were looking for?

Link to comment
Share on other sites

That's fantastic, it works like a charm, many thanks!

 

I noticed in your code there's both 'tweenlite' and 'tweenmax'. I replaced tweenmax with tweenlite and it still works fine - so I guess that's a way to shave some kilobytes off the filesize.

Link to comment
Share on other sites

  • 7 months later...
Sure, I've attached an example. I also had to make one small tweak to the LinePath2D class, so make sure you use the attached one.

 

Is this what you were looking for?

 

Thanks for the great example! However, when I remove this line:

 

TweenMax.to(path, 20, {progress:1});

 

It fails to tween the line altogether. Any ideas why? I'm just looking for something to tween a line's position.

Link to comment
Share on other sites

If I understand you correctly, you should be able to simply tween the x/y of the LinePath2D instance. It's basically a Shape instance.

Link to comment
Share on other sites

Thanks for the quick reply! :D

 

If I understand you correctly, you should be able to simply tween the x/y of the LinePath2D instance. It's basically a Shape instance.

 

I'm sorry, I meant tweening a line's points to move to different positions, similar to the effect in the .fla example, except without the addition of boxes onto the spline/path.

 

I tried commenting out the code that I thought was not needed, ie, everything in this block:

 

//create an array containing 30 blue squares
var boxes:Array = [];
for (i = 0; i < 30; i++) {
boxes.push(createSquare(10, 0x0000FF));
}

//distribute the blue squares evenly across the entire path and set them to autoRotate
path.distribute(boxes, 0, 1, true);

//put a red square exactly halfway through the 2nd segment
path.addFollower(createSquare(10, 0xFF0000), path.getSegmentProgress(2, 0.5));

//tween all of the squares through the path once (wrapping when they reach the end)
TweenMax.to(path, 20, {progress:1});

//while the squares are animating through the path, tween the path's position and rotation too!
//TweenMax.to(path, 3, {rotation:180, x:550, y:400, ease:Back.easeOut, delay:3});

//method for creating squares
function createSquare(size:Number, color:uint=0xFF0000):Shape {
var s:Shape = new Shape();
s.graphics.beginFill(color, 1);
s.graphics.drawRect(-size * 0.5, -size * 0.5, size, size);
s.graphics.endFill();
this.addChild(s);
return s;
}

 

However, if all the code above is removed, the point tweens fail to work. Any idea why this may be?

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