I am playing with the Animation class with a loop in the Commit call, and I'd like to spice it up:
Can I start a (looped) animation in the middle?
Let's say I have the following code that loops an animation in 5 seconds:
// Time in seconds
maxTime = 5;
animation = new Animation(Animation_Handler,
0.0, maxTime * 1000);
animation.Commit(owner: this, name: "test_animation",
length: maxTime * 1000,
repeat: () => true, easing: Easing.Linear);
This will have the handler Animation_Handler start with the argument t = 0 and proceed being called until t = maxTime * 1000, then called again with zero.
Is it possible to start animating with a time t different from zero, avoiding doing it by hand with a time-shift in my animation code?
Related
So I want to have a delay in my Draw function so that when I want to do
Xoffset = Xoffset + 1.5
I do not go from 0 to 30 in a secnd, but I want there to be some delay in the code so that I can easily manage it. I usually use scratch and if you are unfimiliar to that the command for a code delay is
Wait(Insert amount of seconds you want to delay here)
So when you start a part of the code, and the Wait is set to 2, the input will go off, wait 2 seconds, and then move on the the line of code below. I am trying to replicate that but in p5.js.
To detail my comment above, you could do something like this:
var xOffset = 0;
// time delay vars
// current time "snapshot" (imagine pressing the lap time button)
var time;
// the interval to wait between time "snapshots": 2s (2000 milliseconds) in this case
var wait = 2000;
function setup() {
createCanvas(300, 300);
//store the current time
time = millis();
}
function draw() {
background(220);
//check if the difference between now and the previously stored time
// is greater than the wait interval
if(millis() - time >= wait){
console.log(wait, "ms passed");
//if it is, do something
xOffset = xOffset + 1.5;
//also update the stored time
time = millis();
}
circle(xOffset, 150, 60);
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.4.0/p5.min.js"></script>
I'm not sure what the main purpose of the delay is:
is it to slow things down for debugging purposes
is to go into a series a states (e.g. ball moves up for 2s, then right
2s, etc.),
is to animate/smoothly interpolate between two positions within a given
time ?
Maybe something else completely ?
If you simply want to slow things down, perhaps it might be simpler to adjust the frameRate():
var xOffset = 0;
function setup() {
createCanvas(300, 300);
// update 1 frame every two seconds => 0.5 fps
frameRate(0.5);
}
function draw() {
background(220);
xOffset = xOffset + 1.5;
circle(xOffset, 150, 60);
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.4.0/p5.min.js"></script>
Processing still has a delay() function, but p5.js doesn't.
Personally I'm not a fan of blocking behaviour like this and prefer the millis() options even though it's more verbose. That being said, if the program/demo you write is super simple delay() might just be enough.
Even if delay() is missing you could use js setTimeout() with a Promise, but that's getting into more advanced JS as Paul already mentioned:
var xOffset = 0;
function setup() {
createCanvas(300, 300);
// prevent p5's default draw() updates
noLoop();
}
function draw() {
background(220);
xOffset = xOffset + 1.5;
circle(xOffset, 150, 60);
// wait 2s then call draw() manually
delay(2000).then(draw);
}
function delay(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.4.0/p5.min.js"></script>
Now, if you want to create a smooth animation from a start x offset to an end offset in a set amount of seconds you'd need a different approach.
You can find a p5.js example at the of this answer:
var startTime;
var duration = 2000;
var startValue = 0;
var endValue = 300;
var currentValue = startValue;
function setup(){
createCanvas(300, 300);
textAlign(RIGHT);
startTime = millis();
}
function draw(){
background(255);
moveCircle();
drawCircle();
}
function drawCircle() {
circle(currentValue, 150, 60);
}
function moveCircle(){
var progress = (float)(millis()-startTime)/duration;//millis()-startTime = difference in time from start until now
if(progress < 1.0) currentValue = startValue + (endValue * progress);//the current value is the final value scaled/multiplied by the ratio between the current duration of the update and the total duration
}
function mousePressed(){//reset value and time
currentValue = startValue;
startTime = millis();
}
function keyPressed(){//update duration
if(key == '-') if(duration > 0) duration -= 100;
if(key == '=' || key == '+') duration += 100;
console.log("duration: " + duration);
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.4.0/p5.min.js"></script>
And if you're comfortable using external libraries you could simply use a tweening library such as gsap:
var x = 0;
function setup(){
createCanvas(300, 300);
// animate "this" global object's x property to 300 in 2 seconds
gsap.to(this, {x: 300, duration: 2});
}
function draw(){
background(255);
circle(x, 150, 60);
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.4.0/p5.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.8.0/gsap.min.js"></script>
Notice the animation has a bit of easing (default ease out) which could be nice.
In JavaScript, unlike Scratch, there is not a good way to delay synchronous* code. The reason for this is that all the functionality of a webpage is run using a single thread. Which means that while there can be multiple sequences of instructions performing different pieces of functionality, only one of those sequences can execute at a time. Therefore if you did something to delay the execution of JavaScript within the draw() function, it would block all other functionality, making the browser tab unresponsive (and potentially causing the browser to halt your JavaScript altogether).
Instead of pausing a series of JavaScript statements like one would do in Scratch, it is necessary to either use the amount of time that has elapsed as part of a condition that determines whether some code should run (which is what the example George shared in the comments does), or to use the built in setTimeout() function to schedule some code to run some number of milliseconds in the future (but you would not want to call setTimeout() from draw() because it would repeatedly schedule the code every frame).
* note: there are also ways to create delays using asynchronous javascript code, but this is a more advanced topic
I wrote the following little demo that rotates a UIView 360° by rotating it 90° at a time. That means the animation has 4 steps
I wanted it to ease in on the first animation step, go at a steady pace for the middle 2 steps, and then use ease out timing for the last step so it coasts to a stop. The code is below. Here is the animation timing it uses:
Animating to 90°, options = curveEaseIn
Animating to 180°, options = curveLinear
Animating to 270°, options = curveLinear
Animating to 0°, options = curveEaseOut
Each step takes 1/2 second, for a total duration of 2 seconds. However, since the first and last steps take 1/2 second but start/end at a slower pace, the "full speed" part of those animation steps is noticeably faster than the middle 2 animation steps that use linear timing. The animation is not smooth as a result.
Is there an easy way to adjust the step timing so each of the steps in the animation runs at the same pace as the beginning/end step that has ease in/ease out timing?
I guess I could instead create a keyframe animation where the entire animation uses ease-in/ease-out timing, and the intermediate steps inside the animation use linear timing. It seems like there should be an easy way to get that out of step-wise animation however.
class ViewController: UIViewController {
var rotation: CGFloat = 0
#IBOutlet weak var rotateableView: RotatableView!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
#IBAction func handleRotateButton(_ sender: UIButton? = nil) {
let duration = 0.5
var optionsValue: (options: UIView.AnimationOptions, optionsName: String) = (.curveLinear, "curveLinear")
self.rotation = fmod(self.rotation + 90, 360)
sender?.isEnabled = false
if self.rotation == 0 {
optionsValue = (.curveEaseOut, "curveEaseOut") // Ending the animatino, so ease out.
} else if self.rotation == 90 {
optionsValue = (.curveEaseIn, "curveEaseIn") // Beginning the animation, so ease in
}
let rotation = String(format:"%3.0f", self.rotation)
print("Animating to \(rotation)°, options = \(optionsValue.optionsName)")
UIView.animate(withDuration: duration, delay: 0, options: optionsValue.options) {
let angle = self.rotation / 180.0 * CGFloat.pi
self.rotateableView.transform = CGAffineTransform.init(rotationAngle: angle)
} completion: { finished in
if self.rotation != 0 {
self.handleRotateButton(sender)
} else {
self.rotation = 0
sender?.isEnabled = true
}
}
}
}
Ok, I couldn't figure out how to adjust the timing of a series of step-wise animations to get them to ease into the first step, use linear timing for the intermediate steps, and use ease out on the final steps.
However, keyframe animation apparently defaults to ease-in, ease-out timing for the whole animation. That makes creating ease-in, ease-out timing for a whole sequence of steps very easy. It looks like this:
#IBAction func handleRotateButton(_ sender: UIButton? = nil) {
UIView.animateKeyframes(withDuration: 1.5, delay: 0, options: []) {
for index in 1...4 {
let startTime = Double(index-1) / 4
UIView.addKeyframe(withRelativeStartTime: startTime,
relativeDuration: 0.25,
animations: {
let angle: CGFloat = CGFloat(index) / 2 * CGFloat.pi
self.rotateableView.transform = CGAffineTransform.init(rotationAngle: angle)
}
)
}
} completion: { completed in
self.rotation = 0
sender?.isEnabled = true
}
}
Further, apparently you can pass in the same animation curve flags into UIView.animateKeyframes() that you can use for the options to animate(withDuration:delay:options:animations:completion:), if you simply create UIView.KeyframeAnimationOptions using the raw value from UIView.AnimationOptions. You can do that with an extension like this:
extension UIView.KeyframeAnimationOptions {
init(animationOptions: UIView.AnimationOptions) {
self.init(rawValue: animationOptions.rawValue)
}
}
Then you can use code like this to create KeyframeAnimationOptions from AnimationOptions:
let keyframeOptions = UIView.KeyframeAnimationOptions(animationOptions: .curveLinear)
If i'm using OSCdef to listen for changes from a function, such as:
OSCdef('listen', {
arg msg;
~trigger = msg[5]; // This value is 0 when off, 1 when on
~amp = msg[3].linexp(0.0, 1.0, 0.7, 0.8 );
~dur = msg[4].linexp(0.1, 1.0, 1.0, 0.01);
~pitch = msg[4].linlin(0.0, 1.0, 80, 800);
}, '/ctrl');
When ~trigger fires, the variable becomes 1. I want to play a synth or open an env to change the sound.
However, when ~trigger fires, it fires for 10 seconds randomly, so you'll have 1,1,0,1,0,0,0,1,0,01,01,01,01,1,01, etc for 10 seconds.
I want to know if it's possible to catch the first 1, play an event and ignore the remaining triggers for the next 10 seconds
Worth looking at "suppressing triggers":
Passing or suppressing triggers: You might need to generate triggers
continuously, but permit the triggers to take effect only when a
condition is met. Multiplication handles this nicely: condition *
trigger. Since the condition evaluates as 0 when false, the trigger
will be replaced by 0 and nothing happens, as desired.\ \ For a simple
case, let’s refine the mic amplitude example by suppressing triggers
that occur within 1/4 second after the previous. var mic = In.ar(8,
1), amplitude = Amplitude.kr(mic), trig = amplitude > 0.2,
timer = Timer.kr(trig), // how long since the last trigger?
filteredTrig = (timer > 0.25) * trig;
SendTrig.kr(filteredTrig, 0, amplitude);
Source: https://supercollider.github.io/tutorials/If-statements-in-a-SynthDef.html
So i just wanted to make a generator that generates a random value every 10 seconds. And instead it generates a random value every frame after 10 seconds of waiting. Please correct me.
var imgs = [];
var a = 0
function randomizea() {
a = int(random(5));
}
function setup() {
createCanvas(1400, 850);
// for (var i=0; i<5; i++) {
// imgs[i] = loadImage("data/img"+i+".png");
// }
}
function draw() {
background(150, 100, 150);
setInterval(randomizea, 1000);
// image(imgs[a], 0, 0);
text(a, 0, 50);
}
You should generally not use setInterval() with P5.js. Instead, rely on the timing mechanisms that P5.js already gives you.
Here's a simple example that would print something to the console every 10 seconds:
function draw(){
if(frameCount % 600 == 0){
console.log("here");
}
}
Since 60 frames fire per second, then 600 frames is 10 seconds. We use the % modulus operator to check that the frameCount variable is a multiple of 600, which means that we're at a multiple of 10 seconds.
You could also use the millis() function and check whether a certain time has elapsed.
Related posts:
How to make a delay in processing project?
How can I draw only every x frames?
Removing element from ArrayList every 500 frames
Timing based events in Processing
How to add +1 to variable every 10 seconds in Processing?
How to create something happen when time = x
making a “poke back” program in processing
Processing: How do i create an object every “x” time
Timer using frameRate and frame counter reliable?
Adding delay in Processing
Please also consult the P5.js reference for more information.
I have a question about the performance of SVG animations.
When I set de duration of an animation of an rectangle which should move frome one coordinate to another, the real time the animation takes is different from the time which I set in the animation. If the system/browser is very busy the real time the animation takes increases. I am working with the SVG-Library Raphael.js. Here you can see a code sample in which I tried to illustrate the problem:
var paper = new Raphael("paperDiv");
var rectangle = paper.rect(200,200,30,10);
rectangle.attr({
x: 50,
y: 50
});
window.onload = function(){
var d = new Date();
var datum = d.getTime();
rectangle.animate({x: 500, y: 50}, 1000, "linear", function(){
d = new Date();
alert("time the animation should take: " + 1000 + "; time the animation really takes: " + (d.getTime() - datum));
});
}
Is there any way to set a real animation time?
Many thanks for your answers!