How to get PNG image data from a Canvas in Flutter? - dart

I have a Flutter widget that accepts user input and draws to a canvas using a custom painter:
class SPPoint {
final Point point;
final double size;
SPPoint(this.point, this.size);
String toString() => "SPPoint $point $size";
}
class SignaturePadPainter extends CustomPainter {
final List<SPPoint> allPoints;
final SignaturePadOptions opts;
Canvas _lastCanvas;
Size _lastSize;
SignaturePadPainter(this.allPoints, this.opts);
ui.Image getPng() {
if (_lastCanvas == null) {
return null;
}
if (_lastSize == null) {
return null;
}
var recorder = new ui.PictureRecorder();
var origin = new Offset(0.0, 0.0);
var paintBounds = new Rect.fromPoints(_lastSize.topLeft(origin), _lastSize.bottomRight(origin));
var canvas = new Canvas(recorder, paintBounds);
paint(canvas, _lastSize);
var picture = recorder.endRecording();
return picture.toImage(_lastSize.width.round(), _lastSize.height.round());
}
paint(Canvas canvas, Size size) {
_lastCanvas = canvas;
_lastSize = size;
for (var point in this.allPoints) {
var paint = new Paint()..color = colorFromColorString(opts.penColor);
paint.strokeWidth = 5.0;
var path = new Path();
var offset = new Offset(point.point.x, point.point.y);
path.moveTo(point.point.x, point.point.y);
var pointSize = point.size;
if (pointSize == null || pointSize.isNaN) {
pointSize = opts.dotSize;
}
canvas.drawCircle(offset, pointSize, paint);
paint.style = PaintingStyle.stroke;
canvas.drawPath(path, paint);
}
}
bool shouldRepaint(SignaturePadPainter oldDelegate) {
return true;
}
}
Currently currently getPng() returns a dart:ui Image object, but I can't tell how to get the bytes from the image data (if this is even possible)

Here's the solution I came up with, now that toByteData() was added to the SDK:
var picture = recorder.endRecording();
var image =
picture.toImage(lastSize.width.round(), lastSize.height.round());
ByteData data = await image.toByteData(format: ui.ImageByteFormat.png);
return data.buffer.asUint8List();
This solution is working and has now been published to pub as part of the signature_pad_flutter package: https://github.com/apptreesoftware/signature-pad-dart/blob/master/signature_pad_flutter/lib/src/painter.dart#L17

Related

Print WebView with multiple pages in Xamarin UWP

I am trying to print web page in xamarin forms. I am using DependencyService to print webview, which I have implemented in android successfully.
For Windows UWP,
I referred to this link:
https://forums.xamarin.com/discussion/91163/problem-with-printing-webview-in-uwp-phone
The approach used in this is printing only the first page of the webpage.
Edit :
I created an interface IPrint providing only the html source to the function.
public interface IPrint
{
void PrintAsync(string htmlSource);
}
In PrintAsync function (in Windows UWP project),
async void IPrint.PrintAsync(string htmlSource)
{
ViewToPrint.NavigateToString(htmlSource);
ViewToPrint.LoadCompleted += ViewToPrint_LoadCompleteAsync;
}
When WebView is completely loaded,
private async void ViewToPrint_LoadCompleteAsync(object sender, Windows.UI.Xaml.Navigation.NavigationEventArgs e)
{
if (PrintDoc != null)
{
printDoc.AddPages -= PrintDoc_AddPages;
printDoc.GetPreviewPage -= PrintDoc_GetPreviewPage;
printDoc.Paginate -= PrintDoc_Paginate;
}
this.printDoc = new PrintDocument();
try
{
printDoc.AddPages += PrintDoc_AddPages;
printDoc.GetPreviewPage += PrintDoc_GetPreviewPage;
printDoc.Paginate += PrintDoc_Paginate;
bool showprint = await PrintManager.ShowPrintUIAsync();
}
catch (Exception ex)
{
Debug.WriteLine(ex.ToString());
}
PrintDoc = null;
GC.Collect();
}
To add pages in PrintDocument,
private void PrintDoc_AddPages(object sender, AddPagesEventArgs e)
{
printDoc.AddPage(ViewToPrint);
printDoc.AddPagesComplete();
}
To implement multiple pages printing,
I referred this link : https://stackoverflow.com/a/17222629/6366591
I changed AddPages function to the following, but it doesn't seem to work for me.
private void PrintDoc_AddPages(object sender, AddPagesEventArgs e)
{
rectangleList = GetWebPages(ViewToPrint, new Windows.Foundation.Size(100d, 150d));
foreach (Windows.UI.Xaml.Shapes.Rectangle rectangle in rectangleList)
{
printDoc.AddPage(rectangle);
}
printDoc.AddPagesComplete();
}
You can find GetWebPages() function here.
List<Windows.UI.Xaml.Shapes.Rectangle> GetWebPages(Windows.UI.Xaml.Controls.WebView webView, Windows.Foundation.Size page)
{
// ask the content its width
var _WidthString = webView.InvokeScriptAsync("eval",
new[] { "document.body.scrollWidth.toString()" }).GetResults();
int _ContentWidth;
if (!int.TryParse(_WidthString, out _ContentWidth))
throw new Exception(string.Format("failure/width:{0}", _WidthString));
webView.Width = _ContentWidth;
// ask the content its height
var _HeightString = webView.InvokeScriptAsync("eval",
new[] { "document.body.scrollHeight.toString()" }).GetResults();
int _ContentHeight;
if (!int.TryParse(_HeightString, out _ContentHeight))
throw new Exception(string.Format("failure/height:{0}", _HeightString));
webView.Height = _ContentHeight;
// how many pages will there be?
var _Scale = page.Width / _ContentWidth;
var _ScaledHeight = (_ContentHeight * _Scale);
var _PageCount = (double)_ScaledHeight / page.Height;
_PageCount = _PageCount + ((_PageCount > (int)_PageCount) ? 1 : 0);
// create the pages
var _Pages = new List<Windows.UI.Xaml.Shapes.Rectangle>();
for (int i = 0; i < (int)_PageCount; i++)
{
var _TranslateY = -page.Height * i;
var _Page = new Windows.UI.Xaml.Shapes.Rectangle
{
Height = page.Height,
Width = page.Width,
Margin = new Windows.UI.Xaml.Thickness(5),
Tag = new Windows.UI.Xaml.Media.TranslateTransform { Y = _TranslateY },
};
_Page.Loaded += (s, e) =>
{
var _Rectangle = s as Windows.UI.Xaml.Shapes.Rectangle;
var _Brush = GetWebViewBrush(webView);
_Brush.Stretch = Windows.UI.Xaml.Media.Stretch.UniformToFill;
_Brush.AlignmentY = Windows.UI.Xaml.Media.AlignmentY.Top;
_Brush.Transform = _Rectangle.Tag as Windows.UI.Xaml.Media.TranslateTransform;
_Rectangle.Fill = _Brush;
};
_Pages.Add(_Page);
}
return _Pages;
}
WebViewBrush GetWebViewBrush(Windows.UI.Xaml.Controls.WebView webView)
{
// resize width to content
var _OriginalWidth = webView.Width;
var _WidthString = webView.InvokeScriptAsync("eval",
new[] { "document.body.scrollWidth.toString()" }).GetResults();
int _ContentWidth;
if (!int.TryParse(_WidthString, out _ContentWidth))
throw new Exception(string.Format("failure/width:{0}", _WidthString));
webView.Width = _ContentWidth;
// resize height to content
var _OriginalHeight = webView.Height;
var _HeightString = webView.InvokeScriptAsync("eval",
new[] { "document.body.scrollHeight.toString()" }).GetResults();
int _ContentHeight;
if (!int.TryParse(_HeightString, out _ContentHeight))
throw new Exception(string.Format("failure/height:{0}", _HeightString));
webView.Height = _ContentHeight;
// create brush
var _OriginalVisibilty = webView.Visibility;
webView.Visibility = Windows.UI.Xaml.Visibility.Visible;
var _Brush = new WebViewBrush
{
SourceName = webView.Name,
Stretch = Windows.UI.Xaml.Media.Stretch.Uniform
};
_Brush.Redraw();
// reset, return
webView.Width = _OriginalWidth;
webView.Height = _OriginalHeight;
webView.Visibility = _OriginalVisibilty;
return _Brush;
}
#Jerry Nixon's method worked well on my side. Since his code sample was posted on that thread about five years ago. For current UWP APIs, I just done a little changes(e.g, webView.InvokeScriptAsync). I also saw that you call the webView.InvokeScriptAsync method in your code. That's good. But you call the GetResults() method, I did not suggest you call GetResults() method. Because invoking javascript code sometimes will take you a lot of time. You might get the exception A method was called at an unexpected time.
Then, I also noticed that your printing flow is a bit of a mess. Please read Print from your app to learn the standardized printing process.
You could check the official code sample Printing sample for details.
The following was the updated code of your code snippet:
async Task<List<Windows.UI.Xaml.Shapes.Rectangle>> GetWebPages(Windows.UI.Xaml.Controls.WebView webView, Windows.Foundation.Size page)
{
// ask the content its width
var _WidthString = await webView.InvokeScriptAsync("eval",
new[] { "document.body.scrollWidth.toString()" });
int _ContentWidth;
if (!int.TryParse(_WidthString, out _ContentWidth))
throw new Exception(string.Format("failure/width:{0}", _WidthString));
webView.Width = _ContentWidth;
// ask the content its height
var _HeightString = await webView.InvokeScriptAsync("eval",
new[] { "document.body.scrollHeight.toString()" });
int _ContentHeight;
if (!int.TryParse(_HeightString, out _ContentHeight))
throw new Exception(string.Format("failure/height:{0}", _HeightString));
webView.Height = _ContentHeight;
// how many pages will there be?
var _Scale = page.Width / _ContentWidth;
var _ScaledHeight = (_ContentHeight * _Scale);
var _PageCount = (double)_ScaledHeight / page.Height;
_PageCount = _PageCount + ((_PageCount > (int)_PageCount) ? 1 : 0);
// create the pages
var _Pages = new List<Windows.UI.Xaml.Shapes.Rectangle>();
for (int i = 0; i < (int)_PageCount; i++)
{
var _TranslateY = -page.Height * i;
var _Page = new Windows.UI.Xaml.Shapes.Rectangle
{
Height = page.Height,
Width = page.Width,
Margin = new Windows.UI.Xaml.Thickness(5),
Tag = new Windows.UI.Xaml.Media.TranslateTransform { Y = _TranslateY },
};
_Page.Loaded +=async (s, e) =>
{
var _Rectangle = s as Windows.UI.Xaml.Shapes.Rectangle;
var _Brush = await GetWebViewBrush(webView);
_Brush.Stretch = Windows.UI.Xaml.Media.Stretch.UniformToFill;
_Brush.AlignmentY = Windows.UI.Xaml.Media.AlignmentY.Top;
_Brush.Transform = _Rectangle.Tag as Windows.UI.Xaml.Media.TranslateTransform;
_Rectangle.Fill = _Brush;
};
_Pages.Add(_Page);
}
return _Pages;
}
async Task<WebViewBrush> GetWebViewBrush(Windows.UI.Xaml.Controls.WebView webView)
{
// resize width to content
var _OriginalWidth = webView.Width;
var _WidthString = await webView.InvokeScriptAsync("eval",
new[] { "document.body.scrollWidth.toString()" });
int _ContentWidth;
if (!int.TryParse(_WidthString, out _ContentWidth))
throw new Exception(string.Format("failure/width:{0}", _WidthString));
webView.Width = _ContentWidth;
// resize height to content
var _OriginalHeight = webView.Height;
var _HeightString = await webView.InvokeScriptAsync("eval",
new[] { "document.body.scrollHeight.toString()" });
int _ContentHeight;
if (!int.TryParse(_HeightString, out _ContentHeight))
throw new Exception(string.Format("failure/height:{0}", _HeightString));
webView.Height = _ContentHeight;
// create brush
var _OriginalVisibilty = webView.Visibility;
webView.Visibility = Windows.UI.Xaml.Visibility.Visible;
var _Brush = new WebViewBrush
{
SourceName = webView.Name,
Stretch = Windows.UI.Xaml.Media.Stretch.Uniform
};
_Brush.Redraw();
// reset, return
webView.Width = _OriginalWidth;
webView.Height = _OriginalHeight;
webView.Visibility = _OriginalVisibilty;
return _Brush;
}
I used the Printing sample and put my above updated code in it and do some relevant changes, then I could print all web pages successfully.

for embedded PDFs, can PDFJS support both scrolling and jump to a page number at the same time?

This seems like it should be very standard behavior.
I can display a scrollable PDF with:
var container = document.getElementById('viewerContainer');
var pdfViewer = new PDFJS.PDFViewer({
container: container,
});
PDFJS.getDocument(DEFAULT_URL).then(function (pdfDocument) {
pdfViewer.setDocument(pdfDocument);
});
and I can display the PDF page by page with something like:
PDFJS.getDocument(URL_ANNOTATED_PDF_EXAMPLE).then(function getPdfHelloWorld(pdf) {
pdf.getPage(pageNumber).then(function getPageHelloWorld(page) {
var scale = 1.5;
var viewport = page.getViewport(scale);
var canvas = document.getElementById('the-canvas');
var context = canvas.getContext('2d');
canvas.height = viewport.height;
canvas.width = viewport.width;
var renderContext = {
canvasContext: context,
viewport: viewport
};
page.render(renderContext);
});
But can't seem to find any reference in the API to both allow scrolling and jumping to a particular page, besides:
pdfViewer.currentPageNumber = 3;
which doesn't work...
So I found a way to make this work (mixed with a little Angular code, "$scope.$watch...") I now have other problems with font decoding. But here is a solution that might help someone else.
var me = this;
PDFJS.externalLinkTarget = PDFJS.LinkTarget.BLANK;
var container = document.getElementById('capso-court-document__container');
function renderPDF(url, container) {
function renderPage(page) {
var SCALE = 1;
var pdfPageView = new PDFJS.PDFPageView({
container: container,
id: page.pageIndex + 1,
scale: SCALE,
defaultViewport: page.getViewport(SCALE),
textLayerFactory: new PDFJS.DefaultTextLayerFactory(),
annotationLayerFactory: new PDFJS.DefaultAnnotationLayerFactory()
});
pdfPageView.setPdfPage(page);
return pdfPageView.draw();
}
function renderPages(pdfDoc) {
var pageLoadPromises = [];
for (var num = 1; num <= pdfDoc.numPages; num++) {
pageLoadPromises.push(pdfDoc.getPage(num).then(renderPage));
}
return $q.all(pageLoadPromises);
}
PDFJS.disableWorker = true;
return PDFJS.getDocument(url)
.then(renderPages);
}
$scope.$watch(function() {
return {
filingUrl: me.filingUrl,
whenPageSelected: me.whenPageSelected,
};
}, function(newVal, oldVal) {
if (newVal.filingUrl) {
//newVal.filingUrl = URL_EXAMPLE_PDF_ANNOTATED;
//newVal.filingUrl = URL_EXAMPLE_PDF_ANNOTATED_2;
//newVal.filingUrl = URL_EXAMPLE_PDF_MULTI_PAGE;
if (newVal.filingUrl !== oldVal.filingUrl &&
newVal.whenPageSelected &&
newVal.whenPageSelected.page) {
scrollToPage(newVal.whenPageSelected.page);
}
//HACK - create new container for each newly displayed PDF
container.innerHTML = '';
var newContainerForNewPdfSelection = document.createElement('div');
container.appendChild(newContainerForNewPdfSelection);
renderPDF(newVal.filingUrl, newContainerForNewPdfSelection).then(function() {
if (newVal.whenPageSelected &&
newVal.whenPageSelected.page) {
scrollToPage(newVal.whenPageSelected.page);
}
});
}
}, true);
function scrollToPage(pageNumber) {
var pageContainer = document.getElementById('pageContainer' + pageNumber);
if (pageContainer) {
container.scrollTop = pageContainer.offsetTop;
} else {
console.warn('pdf pageContainer doesn\'t exist for index', pageNumber);
}
}

How to perform Image comparison in Appium?

I need one help.
I am using Appium and I have a scenerio wherein I need to capture an image from the app under test, preserve that image as a baseline image and then capture another image from that app under test and then perform an image comparison between these two images.
Can anyone guide me how to do this please.
This is how you do it:
#Test(dataProvider = "search")
public void eventsSearch(String data) throws InterruptedException, IOException {
Thread.sleep(2000);
boolean status = false;
//WebElement img = driver.findElementByClassName("android.widget.ImageView");
//take screen shot
File screen = ((TakesScreenshot) driver)
.getScreenshotAs(OutputType.FILE);
// for (String event : events) {
driver.findElementById("your id")
.sendKeys(data);
driver.hideKeyboard();
List<WebElement> list = driver
.findElementsByXPath("//*[#class='android.widget.TextView' and #index='1']");
System.out.println(list.size());
int i = 0;
for (WebElement el : list) {
String eventList = el.getText();
System.out.println("events" + " " + eventList);
if (eventList.equals("gg")) {
status = true;
break;
}
i++;
}
//capture image of searched contact icon
List<WebElement > imageList = driver.findElementsByXPath("//*[#class='android.widget.ImageView' and #index='0']");
System.out.println(imageList.size());
System.out.println(i);
WebElement image = imageList.get(1);
Point point = image.getLocation();
//get element dimension
int width = image.getSize().getWidth();
int height = image.getSize().getHeight();
BufferedImage img = ImageIO.read(screen);
BufferedImage dest = img.getSubimage(point.getX(), point.getY(), width,
height);
ImageIO.write(dest, "png", screen);
File file = new File("Menu.png");
FileUtils.copyFile(screen, file);
//verify images
verifyImage("Menu.png", "Menu.png" );
//Assert.assertTrue(status, "FAIL Event doesn't match" + data);
}
#DataProvider(name = "search")
public Object[][] searchData() {
return new Object[][] { { "gg" } };
}
public void verifyImage(String image1, String image2) throws IOException{
File fileInput = new File(image1);
File fileOutPut = new File(image2);
BufferedImage bufileInput = ImageIO.read(fileInput);
DataBuffer dafileInput = bufileInput.getData().getDataBuffer();
int sizefileInput = dafileInput.getSize();
BufferedImage bufileOutPut = ImageIO.read(fileOutPut);
DataBuffer dafileOutPut = bufileOutPut.getData().getDataBuffer();
int sizefileOutPut = dafileOutPut.getSize();
Boolean matchFlag = true;
if(sizefileInput == sizefileOutPut) {
for(int j=0; j<sizefileInput; j++) {
if(dafileInput.getElem(j) != dafileOutPut.getElem(j)) {
matchFlag = false;
break;
}
}
}
else
matchFlag = false;
Assert.assertTrue(matchFlag, "Images are not same");
}

How to create a "countdown timer" GIF?

I'd want to create a GIF that counts down for 60 seconds. I could use photoshop but don't want to go through the hassle of creating new layers for each number.
I'm looking for a way to automatically generate a GIF (or images that I can combine into a GIF after the fact) that counts down from 60 to 0.
I'll accept any answer that fulfills these requirements.
I post this AIR code as an education exercise to the reader. The base idea is to use ActionScript to render text via the TextField clas within a Sprite, use Flash's ability to render any DisplayObject to static bitmap data and then use a 3rd-party open-source lib to convert each rendered frame to a gif.
Note: You could save each BitmapData frame to a file so you could use an external gif creation tool.
package {
import flash.display.BitmapData;
import flash.display.Sprite;
import flash.utils.ByteArray;
import flash.text.TextField;
import flash.text.TextFormat;
import flash.filesystem.File;
import flash.filesystem.FileStream;
import flash.filesystem.FileMode;
import org.bytearray.gif.encoder.GIFEncoder;
import org.bytearray.gif.player.GIFPlayer;
public class Main extends Sprite {
var defaultFormat:TextFormat = new TextFormat();
var background:Sprite = new Sprite();
var countdownText = new TextField();
var fsize:int = 125;
var xsize:int = 100;
var ysize:int = 100;
public function Main():void {
defaultFormat.font = "Arial";
defaultFormat.size = fsize;
defaultFormat.color = 0xffffff;
var encoder:GIFEncoder = new GIFEncoder();
encoder.setRepeat(0);
encoder.setDelay(1000);
encoder.start();
setupCounterDisplay();
var startFrom:uint = 60;
var startColor:uint = 255;
for (var i:int = startFrom; i > -1; i--) {
var colorRGB:uint = (startColor / startFrom) * i;
encoder.addFrame(createCounterDisplay(i, ( colorRGB << 16 ) | ( colorRGB << 8 ) | colorRGB ) );
}
encoder.finish();
removeChild(background);
saveGIF("CounterDown.gif", encoder.stream);
playGIF(encoder.stream);
}
private function playGIF(data:ByteArray):void {
data.position = 0;
var player:GIFPlayer = new GIFPlayer();
player.loadBytes(data);
addChild(player);
}
private function saveGIF(fileName:String, data:ByteArray):void {
var outFile:File = File.desktopDirectory;
outFile = outFile.resolvePath(fileName);
var outStream:FileStream = new FileStream();
outStream.open(outFile, FileMode.WRITE);
outStream.writeBytes(data, 0, data.length);
outStream.close();
}
private function padString(string:String, padChar:String, finalLength:int, padLeft:Boolean = true):String {
while (string.length < finalLength) {
string = padLeft ? padChar + string : string + padChar;
}
return string;
}
private function setupCounterDisplay():void {
var xsize:int = 100;
var ysize:int = 100;
background.graphics.beginFill(0x000000, 1);
background.graphics.drawCircle(xsize, ysize, ysize);
background.graphics.endFill();
countdownText.defaultTextFormat = defaultFormat;
countdownText.border = true;
countdownText.borderColor = 0xff0000;
background.addChild(countdownText);
this.addChild(background);
}
private function createCounterDisplay(num:int, color:uint):BitmapData {
background.graphics.beginFill(0x000000, 1);
background.graphics.drawCircle(xsize, ysize, ysize);
background.graphics.endFill();
defaultFormat.color = color;
countdownText.defaultTextFormat = defaultFormat;
countdownText.text = padString(num.toString(), "0", 2);
countdownText.autoSize = "center";
countdownText.x = countdownText.width / 5;
countdownText.y = countdownText.height / 5;
var bitmap:BitmapData = new BitmapData(countdownText.width * 1.5, countdownText.height * 1.5, true);
bitmap.draw(background);
return bitmap;
}
}
}
Gif library via : https://code.google.com/p/as3gif/wiki/How_to_use

Titanium ImageView animation width and height

I try to create an ImageView in Titanium like this:
var animationView = Titanium.UI.createImageView
(
{
images:animationImages,
duration:50,
repeatCount:0,
width: '90dp',
height: '270dp'
}
);
On android it gets its size as expected, but on IOS, it simply doesn't gets scaled. Is there something, i'm doing wrong? Or should i do it frame by frame by creating the ImageViews manually then changing them with setInterval?
This is actually not a consistent solution, it should be a comment, but since I don't have enough rep I have to write it as an answer.
For starters I would try to give it a top and left properties.
Secondly, are those images retrieved from a remote URL? Remote URLs are only supported in Android. If that is the case you could do a workaround as you said in the question.
Finally, the 'dp' only works for android, so it won't scale at all in iOS, it will simply erase the 'dp' and use the number as "points", on non-retina screens it will be the same number of pixels and on a retina display it will be the double.
I finally decided to create my own animation class, which look like this:
function Animation(data)
{
var width = data.hasOwnProperty("width") ? data.width : Ti.UI.SIZE;
var height = data.hasOwnProperty("height") ? data.height: Ti.UI.SIZE;
var duration = data.hasOwnProperty("duration") ? data.duration : 50;
var imageFiles = data.hasOwnProperty("images") ? data.images : [];
var images = [];
var container = Ti.UI.createView
(
{
width:width,
height: height
}
);
for(var i=0; i<imageFiles.length; i++)
{
var image = Ti.UI.createImageView
(
{
image:imageFiles[i],
width:width,
height:height
}
);
if(i!=0)
image.setVisible(false);
container.add(image);
images.push(image);
}
container.activeImage = 0;
container.intervalId = null;
container.setActiveImage = function(index)
{
if(container.intervalId == null)
container.activeImage = index;
}
container.start = function()
{
var callback = function()
{
for(var i=0; i<images.length; i++)
{
if(i == container.activeImage)
images[i].setVisible(true);
else
images[i].setVisible(false);
}
container.activeImage = (container.activeImage + 1) % images.length;
}
container.intervalId = setInterval ( callback, duration );
}
container.stop = function()
{
clearInterval(container.intervalId);
container.intervalId = null;
}
return container;
}
module.exports = Animation;
And you can use it like this:
var Animation = require('...path to your animation file');
var myAnimation = new Animation
(
{
width:'100dp',
height:'100dp',
duration:50, //duration while one frame is showing
images:['one.png', 'two.png'...], //full paths
}
);
//start:
myAnimation.start();
//stop
myAnimation.stop();

Resources