Jump to content
Search Community

Scrolling on mobile devices using AS3 AIR?

Big Bad Roo test
Moderator Tag

Recommended Posts

Gidday Jack

 

I'm using Flash Pro and AIR to create a native mobile app for iOS and Android, and have JUST realised that scrolling doesn't happen automatically - you have to program that little devil.

 

What of your products would I need to do this? ThrowProps? And what else?

 

Do you have any as3 coding examples that can get me up and running with vertically scrolling lists that contain text and images?

 

Thanks for your time and help.

Link to comment
Share on other sites

Hi,

 

Just to be clear, you don't need any GreenSock products for scrolling on mobile devices. Your app can be significantly enhanced though by using ThrowPropsPlugin and BlitMask.

 

ThrowPropsPlugin gives you extremely accurate velocity-based tweens. BlitMask allows you to cache a very large displayObject as a bitmap and only render the visible portion which leads to smoother performance (less pixels rendered, less overhead).

 

On the ThrowProps page and in the ThrowProps docs we provide code samples that you can just copy and paste for instant flick-scrolling.

 

We don't make any GUI components for scrollbars if that's what you need, these might get you started in the right direction:

http://thanksmister.com/2010/10/14/android-as3-scrolling-list/

http://active.tutsplus.com/tutorials/actionscript/create-a-smooth-rolling-scroller-with-as3/

  • Like 1
Link to comment
Share on other sites

Thanks Carl

 

I'm not after the scrollbar versions, just the vertical flick type scrolling, so ThrowProps looks the go.

 

I have been having a play with the demo code on the ThrowProps page, and can get it to work when I copy it verbatim into a frame in Flash Pro.

 

I then tried to set it up as a document class so I could practice incorporating it into my classes, but nothing displays.

 

If I get rid of the BlitMask it works.

 

If possible, would you mind taking a look to see if something obvious is tripping me up? Thanks again mate. If I can get this to cater for my project's scrolling needs, I'll happily become a corporate member.

package  {
	
	import flash.display.MovieClip;
	
	import com.greensock.*; 
	import com.greensock.easing.*;
	import com.greensock.plugins.*;
	import flash.geom.Rectangle;
	import flash.utils.getTimer;
	import flash.events.MouseEvent;
	import flash.text.*;
	import flash.display.*;
	TweenPlugin.activate([ThrowPropsPlugin]);

	
	public class ScrollTestDelete extends MovieClip {
		
		var bounds:Rectangle = new Rectangle(30, 30, 250, 230);
		var mc:Sprite = new Sprite();
		var blitMask:BlitMask = new BlitMask(mc, bounds.x, bounds.y, bounds.width, bounds.height, false);
		
		var t1:uint, t2:uint, y1:Number, y2:Number, yOverlap:Number, yOffset:Number;

		public function ScrollTestDelete() {
			// constructor code
			init();
		}
		
		public function init():void
		{
			addChild(mc);
			setupTextField(mc, bounds);
			
			
			blitMask.addEventListener(MouseEvent.MOUSE_DOWN, mouseDownHandler);
			
		}
		
		function mouseDownHandler(event:MouseEvent):void {
			TweenLite.killTweensOf(mc);
			y1 = y2 = mc.y;
			yOffset = this.mouseY - mc.y;
			yOverlap = Math.max(0, mc.height - bounds.height);
			t1 = t2 = getTimer();
			mc.stage.addEventListener(MouseEvent.MOUSE_MOVE, mouseMoveHandler);
			mc.stage.addEventListener(MouseEvent.MOUSE_UP, mouseUpHandler);
		}
		
		function mouseMoveHandler(event:MouseEvent):void {
			var y:Number = this.mouseY - yOffset;
			//if mc's position exceeds the bounds, make it drag only half as far with each mouse movement (like iPhone/iPad behavior)
			if (y > bounds.top) {
				mc.y = (y + bounds.top) * 0.5;
			} else if (y < bounds.top - yOverlap) {
				mc.y = (y + bounds.top - yOverlap) * 0.5;
			} else {
				mc.y = y;
			}
			blitMask.update();
			var t:uint = getTimer();
			//if the frame rate is too high, we won't be able to track the velocity as well, so only update the values 20 times per second
			if (t - t2 > 50) {
				y2 = y1;
				t2 = t1;
				y1 = mc.y;
				t1 = t;
			}
			event.updateAfterEvent();
		}
		
		function mouseUpHandler(event:MouseEvent):void {
			mc.stage.removeEventListener(MouseEvent.MOUSE_UP, mouseUpHandler);
			mc.stage.removeEventListener(MouseEvent.MOUSE_MOVE, mouseMoveHandler);
			var time:Number = (getTimer() - t2) / 1000;
			var yVelocity:Number = (mc.y - y2) / time;
			ThrowPropsPlugin.to(mc, {throwProps:{
										 y:{velocity:yVelocity, max:bounds.top, min:bounds.top - yOverlap, resistance:300}
									 }, onUpdate:blitMask.update, ease:Strong.easeOut
									}, 10, 0.3, 1);
		}
		
		function setupTextField(container:Sprite, bounds:Rectangle, padding:Number=20):void {
			var tf:TextField = new TextField();
			tf.width = bounds.width - padding;
			tf.x = tf.y = padding / 2;
			tf.defaultTextFormat = new TextFormat("_sans", 12);
			tf.text = "Click and drag this content and then let go as you're dragging to throw it. Notice how it smoothly glides into place, respecting the initial velocity and the maximum/minimum coordinates.\n\nThrowPropsPlugin allows you to simply define an initial velocity for a property (or multiple properties) as well as optional maximum and/or minimum end values and then it will calculate the appropriate landing position and plot a smooth course based on the easing equation you define (Quad.easeOut by default, as set in TweenLite). This is perfect for flick-scrolling or animating things as though they are being thrown.\n\nFor example, let's say a user clicks and drags a ball and you track its velocity using an ENTER_FRAME handler and then when the user releases the mouse button, you'd determine the velocity but you can't do a normal tween because you don't know exactly where it should land or how long the tween should last (faster initial velocity would mean a longer duration). You need the tween to pick up exactly where the user left off so that it appears to smoothly continue moving at the same velocity they were dragging and then decelerate based on whatever ease you define in your tween.\n\nAs demonstrated here, maybe the final resting value needs to lie within a particular range so that the content doesn't land outside a particular area. But you don't want it to suddenly jerk to a stop when it hits the edge; instead, you want it to ease gently into place even if that means going past the landing spot briefly and easing back (if the initial velocity is fast enough to require that). The whole point is to make it look smooth.\n\nThrowPropsPlugin isn't just for tweening x and y coordinates. It works with any numeric property, so you could use it for spinning the rotation of an object as well. Or the scaleX/scaleY properties. Maybe the user drags to spin a wheel and lets go and you want it to continue increasing the rotation at that velocity, decelerating smoothly until it stops.\n\nOne of the trickiest parts of creating a throwProps tween that looks fluid and natural, particularly if you're applying maximum and/or minimum values, is determining its duration. Typically it's best to have a relatively consistent level of resistance so that if the initial velocity is very fast, it takes longer for the object to come to rest compared to when the initial velocity is slower. You also may want to impose some restrictions on how long a tween can last (if the user drags incredibly fast, you might not want the tween to last 200 seconds). The duration will also affect how far past a max/min boundary the property can potentially go, so you might want to only allow a certain amount of overshoot tolerance. That's why ThrowPropsPlugin has a few static helper methods that make managing all these variables much easier. The one you'll probably use most often is the to() method which is very similar to TweenLite.to() except that it doesn't have a duration parameter and it adds several other optional parameters.\n\nA unique convenience of ThrowPropsPlugin compared to most other solutions out there which use ENTER_FRAME loops is that everything is reverseable and you can jump to any spot in the tween immediately. So if you create several throwProps tweens, for example, and dump them into a TimelineLite, you could simply call reverse() on the timeline to watch the objects retrace their steps right back to the beginning.\n\nThe overshootTolerance parameter sets a maximum number of seconds that can be added to the tween's duration (if necessary) to accommodate temporarily overshooting the end value before smoothly returning to it at the end of the tween. This can happen in situations where the initial velocity would normally cause it to exceed the max or min values. An example of this would be in the iOS (iPhone or iPad) when you flick-scroll so quickly that the content would shoot past the end of the scroll area. Instead of jerking to a sudden stop when it reaches the edge, the content briefly glides past the max/min position and gently eases back into place. The larger the overshootTolerance the more leeway the tween has to temporarily shoot past the max/min if necessary.";
			tf.multiline = tf.wordWrap = true;
			tf.selectable = false;
			tf.autoSize = TextFieldAutoSize.LEFT;
			container.addChild(tf);
		
			container.graphics.beginFill(0xFFFFFF, 1);
			container.graphics.drawRect(0, 0, tf.width + padding, tf.textHeight + padding);
			container.graphics.endFill();
			container.x = bounds.x;
			container.y = bounds.y;
		
		};

	}
	
}

Link to comment
Share on other sites

It looks like you might need to recapture the BitmapData for blitMask in your init() function. When you are creating the mask outside of the constructor, mc is an empty sprite so blitMask will not have anything to display. Just calling blitMask.update() will only update the BitmapData if the scale/rotation of mc changes.
 
You can force a recapture of the BitmapData using

blitMask.update(null, true);

or you could just wait until the end of init() before initiating blitMask:

var blitMask:BlitMask;

function init() {
  setupTextField(mc, bounds);
  blitMask = new BlitMask(mc, bounds.x, bounds.y, bounds.width, bounds.height, false);
}
Link to comment
Share on other sites

Thanks Jamie - that made it appear.

 

It's only scrolling a miniscule amount and there's no apparent flick action.

 

Anyone out there want a couple hours work helping me to get this up and running? I'll pay via Paypal.

 

What I need -

 

- scrolling for an AIR project for iOS and Android!!!

- blitmask running, as some elements to scroll are quite long

- advice as to how to keep buttons within the scrolling mc still clickable

 

Please PM me if anyone is interested.

 

Cheers

Link to comment
Share on other sites

Feel free to post your fla and AS class files in a zip. We'll gladly look at them and help you get the tools working as they should. Its much easier than trying to read lots of code.

Just don't include your GreenSock files (which in your case includes the members-only ThrowPropsPlugin and others). 

 

To be clear you would only need to purchase the Business Green package (which includes all the bonus plugins and a commercial license) if you intend to have the app sold to multiple users. Learn more: http://www.greensock.com/licensing/

Link to comment
Share on other sites

Cheers Carl

 

I JUST got it working.

 

I have an instance of a movieclip called _home that I place the clip to be scrolled into, so I had to code:

 

yOffset = _home.mouseY - mc.y;

 

...instead of the default...

 

yOffset = this.mouseY - mc.y;

 

(and had to do the same elsewhere in the code).

 

So now - couple of questions if you are able to please take a look...?

 

1. Should I also be placing the blitmask inside the _home clip? I thought no since it's not being added using addChild anyway.

 

2. Is there already a way to allow buttons within a clip within a blitmask to be clicked? I experimented with MyEmpty's non-blitmask way, but am wondering if Greensock already has a way before I spend too much more time playing around with blitMask.bitmapMode = false; and testing to see if it's a button click or scroll flick based on drag distance.

 

Thanks mate

Link to comment
Share on other sites

Glad to hear you are making progress.

 

1) I can't really visualize how your project is set up so I don't know if the BlitMask should be inside _home or not. The BlitMask automatically gets added inside the same container as its target.

 

2) When bitmapMode = false you will be able to click interactive elements inside the BlitMask's target object.

 

Currently there is no mechanism in BlitMask to distinguish between clicks and flicks. The best bet is to use the mouseUpHandler to see if the x/y of the mouse (or touch) has changed enough to warrant a flick. And yes, I would go with MrEmpty's recommendation here: http://forums.greensock.com/topic/4919-throwprops-on-movieclip-with-clickable-movieclips-within/#entry18131

Link to comment
Share on other sites

OK - I've just become a Business Green member. Thanks mate.

 

Re the flick code and buttons, I've ALMOST nailed it I think. I just need a little advice for a final bit of code - where to put...

 

blitMask.bitmapMode = false;

 

...so that it triggers false AFTER the tween has finished. It's so the bitmapMode = false while the clip is resting ie buttons can be clicked. Then my code checks for a drag of over 20 pixels before   bitmapMode = true. Can you please help out there Carl?

 

Cheers mate

Link to comment
Share on other sites

Sure, on your throwProps tween add an onComplete callback that uses BlitMask.disableBitmapMode() 

 

 

ThrowPropsPlugin.to(mc, {throwProps:{
y:{velocity:yVelocity, max:bounds.top, min:bounds.top - yOverlap, resistance:300}
}, onUpdate:blitMask.update, onComplete:blitmask.disableBitmapMode,

ease:Strong.easeOut

}, 10, 0.3, 1);

 

I think that should do it.

Link to comment
Share on other sites

Hi; I was creating iPhone like slider some time ago. Here is the source code: http://bassta.bg/experiments/ActiveSwipe.zip It is ready to use slider. The demo is self-explanary, also the code in com.bassta folder. If you need some help with it, just write me. Btw here is some code to achive the same effect: http://snipplr.com/view/64629/actionscript3-touch-event/ And this is demo http://blog.bassta.bg/wp-content/uploads/2013/05/SimpleDemo.swf it works great on iOS devices and can configure it with BlitMask. It is not exactly what you need, but I hope it can help you some way.

 

blog.bassta.bg/wp-content/uploads/2013/05/SimpleDemo.swf

 

  • Like 1
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...