My goal is to make the wave segments move from the bottom of the screen to the top, while animating the wave. The code I'm using to make the wave comes from paperjs.org:
http://paperjs.org/tutorials/animation/creating-animations/#animating-path-segments
Ideally, I'd like to close the path with points that stay anchored to the bottom left and right of the viewport. This is so I can fill the wave with a solid color.
Simply add points to your path before and after the for-loop that generates your evenly distributed segments:
path.add(new Point(0, 1) * view.size);
// Add 5 segment points to the path spread out
// over the width of the view:
for (var i = 0; i <= amount; i++) {
path.add(new Point(i / amount, 1) * view.size);
}
path.add(new Point(1, 1) * view.size);
This will give you points anchored to the bottom of your view. To account for the new segments, change the for loop in the onFrame function to read:
for (var i = 1; i <= amount+1; i++) {
Related
I'm trying to optimize line drawings on a QWidget. I basically have a grid in the background and drawings on top of the grid. Currently I'm redrawing the background and grid lines every time the paint event is called. This works fine if the grid lines are far enough apart so I don't have to draw that many lines, but if the scale gets changed, the lines must be redrawn at that new scale. Also, if the window is resized, then more of the grid is displayed, hurting the performance even more.
Here is the code for drawing the grid:
// draw grid
painter.fillRect(0,0,areaWidth, areaHeight, QColor(255,255,255));
painter.setPen(QPen(QBrush(QColor(240,240,255)), 1, Qt::SolidLine, Qt::FlatCap));
int numXLines = areaWidth/mSIToPixelScale + 1;
int numYLines = areaHeight/mSIToPixelScale + 1;
double width = areaWidth;
double height = areaHeight;
for (int x=0; x<numXLines;x++)
{
for (int y=0; y<numYLines; y++)
{
painter.drawLine(0,y*mSIToPixelScale,width, y*mSIToPixelScale);
painter.drawLine(x*mSIToPixelScale,0,x*mSIToPixelScale,height);
}
}
So when numXLines and numYLines in the above code reach higher values, the performance drops very hard, which makes sense. The grid will always have to be redrawn if the scale changes, but if the scale does not change, then only the drawing on top of the grid should change. How can I accomplish this?
The QWidget::paintEvent( QPaintEvent* aEvent ) is called by the framework not just when you want it. So if you want to remove some lines from the widget than you need draw them conditionally in your function.
For example:
if ( numXLines < 25 && numYLines < 25 )
{
// Draw only every second lines for example.
}
else
{
// Draw all lines.
}
But this is not the best way. Maybe you shall use larger steps between the grid lines if there too many of them.
I found where I went wrong, I was redrawing the y lines every x iteration. I fixed it by creating two separate for loops:
// add grid lines to a painter path
QPainterPath grid;
for (int x=0; x<numXLines;x++)
{
grid.moveTo(x*mSIToPixelScale, 0);
grid.lineTo(x*mSIToPixelScale, height);
}
for (int y=0; y<numYLines; y++)
{
grid.moveTo(0, y*mSIToPixelScale);
grid.lineTo(width,y*mSIToPixelScale);
}
painter.drawPath(grid);
painter.end();
Also, I think drawing onto a QImage first then drawing that image inside the paintEvent would make code more organized, so all your doing in the paintEvent is drawing from a high level.
Looking to animate the below SVG, which I have got working initially. However, I want it to 'draw' the second SVG in the opposite direction, WITH the dots i've defined.
Is there any way I can do this? Effectively drawing my shape from left to right with the dots.
Codepen: http://codepen.io/anon/pen/gFcAz
The normal dash offset animation trick really only works with solid lines.
This is the closest I managed to get using CSS animations.
http://jsfiddle.net/L4zCY/
Unfortunately the dashes crawl because you have no control over the step rate of the stroke-dashoffset. If you could make it step by 10 at a time, the dashes wouldn't move.
So I think the only way around it is to use Javascript.
var path = document.querySelectorAll("svg path").item(0);
animateDashedPath(path);
/*
* Animates the given path element.
* Assumes the path has a "5 5" dash array.
*/
function animateDashedPath(path)
{
var pathLength = path.getTotalLength();
var animationDuration = 2000;
var numSteps = Math.round(pathLength / (5+5) + 1);
var stepDuration = animationDuration / numSteps;
// Build the dash array so we don't have to do it manually
var dasharray = [];
while (numSteps-- > 0) {
dasharray.push(5);
dasharray.push(5);
}
dasharray.push(pathLength);
// Animation start conditions
path.setAttribute("stroke-dasharray", dasharray.join(" "));
path.setAttribute("stroke-dashoffset", -pathLength);
// We use an interval timer to do each step of the animation
var interval = setInterval(dashanim, stepDuration);
function dashanim() {
pathLength -= (5+5);
path.setAttribute("stroke-dashoffset", -pathLength);
if (pathLength <= 0) {
clearInterval(interval);
}
}
}
Demo here
Update
It looks like there is an issue with in FF. If you create the "right" number of dashes for the path length, it doesn't quite reach the end of the path. You need to add extra.
A version of the demo that works properly on FF is here
I have the following problem: I draw a shape on a Group, to be used as a mask. I then rotate an Image in x, y, z, using Rotate3D, and use the Group as the Image's mask, but the result is not what I want, because once the Group becomes the mask of the Image, it acquires its rotation too.
What I want to achieve, is to get a portion of a rotated Image, according to the shape that I have drawn. How can I apply the mask, whithout it be also rotated? Or can this be done any other way?
Here is the code:
grpMask = new Group();
g = grpMask.graphics;
g.lineStyle(0, 0x00FF00);
g.moveTo(points[0].x, points[0].y);
g.beginFill(0x00FF00, .3);
for (var i:int; i < points.length - 1; i++)
{
g.lineTo(points[i + 1].x, points[i + 1].y);
}
g.endFill();
grpCanvas.addElement(grpMask);
I then set imgTarget.mask = grpMask. To imgTarget, have been applied several Rotate3D effects, which unfortunately pass on to the mask.
One solution is to add the object being rotated to a container class like a Sprite or a Group. Then apply the mask to the container, instead of the rotated object:
var container:Group = new Group();
container.addElement(roatatedObject);
container.mask = maskObject;
container.addElement(maskObject);
or
var container:Sprite = new Sprite();
container.addChild(rotatedObject);
container.mask = maskObject;
container.addChild(maskObject);
i want to rotate 3D an Image called img1 in Flex. I want to rotate it around y axis 180 degree. I can do this by using 3D effect already built in Flex but i want to do a bit more different.
I want during rotating, there's another image called img2 appear on back of img1 (in default case, the image appear on the back is img1) and when rotating finish, the image will be img2.
How can i do this ?
Thank you.
If you need no perspective effect, it's quite easy to do. A rough implementation (not tested!):
// Event.ENTER_FRAME event listener
void on_enter_frame(event:Event):void
{
// m_angle is a member of the class/flex component where on_enter_frame is declared
// ANGLE_DELTA is just a constant
m_angle += ANGLE_DELTA;
// Angle clamping to the range [0, PI * 2)
m_angle %= Math.PI * 2;
if (m_angle < 0)
m_angle += Math.PI * 2;
// If we currently look at the front side...
if (m_angle < Math.PI)
{
img1.visible = true;
img2.visible = false;
img1.scaleX = Math.cos(m_angle);
}
else
{
img1.visible = false;
img2.visible = true;
// If you omit negation, the back-side image will be mirrored
img2.scaleX = -Math.cos(m_angle);
}
}
So every frame we increase the rotation angle, clamp it to the range [0, PI * 2). Then depending on the value of the rotation angle, we hide/show the pair of your images, and then perform x-scaling of the visible image.
Thank you, now i found a solution. Please check it here, it's very easy to do.
http://forums.adobe.com/thread/921258
I have a Flex 3 app that has elements that a user can add to the main canvas then resize and reposition.
There are 3 key functions I am using for the resize which are as follows:
When the resize begins:
private function startResize(event:MouseEvent):void
{
RESIZE_START_MOUSE_X = event.localX;
RESIZE_START_MOUSE_Y = event.localY;
RESIZE_START_WIDTH = this.width;
RESIZE_START_HEIGHT = this.height;
RESIZE_START_X = this.x;
RESIZE_START_Y = this.y;
RESIZE_BOUND = calculateResizeBound(event);
addEventListener(MouseEvent.MOUSE_MOVE, resizeMouseHandler);
isResizing = true;
}
When the resize is complete:
private function endResize():void
{
RESIZE_START_MOUSE_X = -1;
RESIZE_START_MOUSE_Y = -1;
RESIZE_START_WIDTH = this.width;
RESIZE_START_HEIGHT = this.height;
RESIZE_START_X = -1;
RESIZE_START_Y = -1;
RESIZE_BOUND = '';
removeEventListener(MouseEvent.MOUSE_MOVE, resizeMouseHandler);
isResizing = false;
}
Whilst the user is resizing:
private function resizeMouseHandler(event:MouseEvent):void
{
var deltaX:Number = event.localX - RESIZE_START_MOUSE_X;
var deltaY:Number = event.localY - RESIZE_START_MOUSE_Y;
if (RESIZE_BOUND.indexOf('T') > -1)
//We are fixing the top so move the bottom edge
{
this.height = RESIZE_START_HEIGHT + deltaY;
}
if (RESIZE_BOUND.indexOf('B') > -1)
//We are fixing the bottom so move the top edge
{
this.y = RESIZE_START_Y + deltaY;
this.height = RESIZE_START_HEIGHT - deltaY;
}
if (RESIZE_BOUND.indexOf('L') > -1)
//We are fixing the left so move the right edge
{
this.width = RESIZE_START_WIDTH + deltaX;
}
if (RESIZE_BOUND.indexOf('R') > -1)
//We are fixing the right so move the left edge
{
this.x = RESIZE_START_X + deltaX;
this.width = RESIZE_START_WIDTH - deltaX;
}
}
There is another function referenced in these called calculateResizeBound(). What this does is return a string indicating which edge / corner should remain fixed during the resize. Eg 'TL' means that the top left corner should stay fixed, 'BR' means bottom right, 'L' means just the left edge etc etc
When the resize starts from the 'normal' position, ie the top left corner stays fixed, everything works great. Similarly with the left or top edges fixed. However for the bottom and right cases, I need to reposition the element at the same time as resizing it since all the co-ordinates are calculated from the top left.
The problem that I have is that when it does this, the resize is not smooth, it keeps jumping up and down slightly as you resize it. Not only that but when you resize from the 'normal' edges the cursor position remains fixed relative to the fixed edge / corner however from one of the other edges, you can see it start to drift away from the edge / corner as you resize.
With this kind of thing, it is easy to get the + / - of the different bits of the calculation muddled but since the resize is working in the correct direction each time, I assume I have these correct.
So presumably the problem is coming from the simultaneous moving and resizing but I can't find a work-around for it. Any thoughts / suggestions would be much appreciated
Doug McCune has an awesome Resize wrapper that you can use to resize elements. Then you just need to add a mover on it. See the blog post for code/sample: http://dougmccune.com/blog/2007/08/17/my-360flex-slides-and-code/