rect
.animate(1000,"<>",0).dmove(50,0)
.animate(1000,"<>",0).dmove(-10,0)
.animate(1000,"<>",0).dmove(20,0)
.animate(1000,"<>",0).dmove(-60,0).loop(true, true);
Why (and should it?) does the loop not repeat the entire animation? He skips 2 and the 3 step.
Demonstration: https://codepen.io/Andreslav/pen/BxGygp
A very similar issue was solved here. The solution can be adapted for your problem:
let w = 100,
h = 100,
t = 1000,
draw = SVG('svg').size('100%', '100%').viewbox(0,0,w,h);
function animation() {
draw.rect(10,10)
.animate(t,">",0).dx(w/2).width(20).height(10)
.animate(t,">",0).dy(h/2).width(15).height(15)
.animate(t,">",0).dx(-w/2).width(10).height(15)
.animate(t,">",0).dy(-h/2).width(10).height(10).after(animation)
}
animation()
It appears the loop only works on the last action.
You can try a different approach to accomplish the same thing by using a path.
E.g: (you'll need to adjust to replicate the same points)
let width = 1000,
height = 100,
draw = SVG('svg').size('100%', '100%').viewbox(0,0,width,height);
// use path to replicate movement
const path = draw.path("M200 0 H 250 H 100 H 120 H 80")
const length = path.length()
path.fill('none').stroke({width:1, color: '#ccc'})
const rect = draw.rect(100, 100)
rect.animate(5000, '<>').during(function(pos, morph, eased){
var p = path.pointAt(eased * length)
rect.center(p.x, p.y)
}).loop(true, true)
(from tutorials)
Related
I am populating 3 arrays and I've no idea which one is being populated (depends on user). My goal is 2 fold.
find which array is being populated
find the Max value Among the 3 arrays (or the arrays which are populated)
eg:
hrGenY = [100,101,102]
cadGenY = [80, 81, 82]
powerGenY = [100,104,106]
This is what I'm doing right now. (This code here is just to determine which array is being populated and NOT nil)
var whichTelemery: [Int] = []
if !powerGenY.isEmpty {
whichTelemery = powerGenY
} else if !hrGenY.isEmpty {
whichTelemery = hrGenY
} else if !cadGenY.isEmpty {
whichTelemery = cadGenY
}
after which I utilise this to determine the X & Y scale for my chart. I use whichever is the Max to normalise the scale.
var yMax: CGFloat = 0.0
var xMax: CGFloat = 0.0
var yMaxTemp: [CGFloat] = [0,0]
var xMaxTemp: [CGFloat] = [0,0]
// Determine yMax & xMax for Scale Resolution
if !dataPointsY.isEmpty {
xMaxTemp[0] = dataPointsX.max()!
yMaxTemp[0] = CGFloat((dataPointsY.max()! >= ftpPlus) ? (1.2*dataPointsY.max()!) : ftpPlus)
xMax = xMaxTemp.max()!
yMax = yMaxTemp.max()!
}
if !whichTelemery.isEmpty && dataPointsY.isEmpty {
xMaxTemp[1] = CGFloat(whichTelemery.count < 900 ? 900 : whichTelemery.count + 1)
yMaxTemp[1] = CGFloat((CGFloat(whichTelemery.max()!) >= ftpPlus) ? (1.2*CGFloat(whichTelemery.max()!)) : ftpPlus)
xMax = xMaxTemp.max()!
yMax = yMaxTemp.max()!
}
The problem with this method is I am making the assumption that the user's power (if populated) will always be higher than the HR(heart rate) which may not be true. When this happens, the HR line will not get drawn as it's above the yMax limit.
The way I'm thinking of doing it is very crude which would involve yet another group of checks to see which of the 3 arrays has the highest number. Would appreciate some better more elegant way.
Thanks
What about getting the max value for your normalisation by doing
let max = [hrGenY.max() ?? 0.0, cadGenY.max() ?? 0.0, powerGenY.max() ?? 0.0].max()
I am creating a game (space shooter) using SpriteKit. Currently adding animations to the game. When explosions spawn, the animation loops at an X number of times (its really random). Sometimes it will loop 3 times and sometimes up to 10 times. The screen ends up being filled up with meaningless explosion animations.
I used to have a simple fade in/fade out animation which was working fine, but have finally upgraded to something smoother. I introduced a for loop and it has given me this issue. i also tried using a while loop with no avail. I have tried using the animation without a sequence but that doesn't fix anything either.
func spawnExplosion(spawnPosition: CGPoint) {
var explosion = SKSpriteNode()
textureAtlas = SKTextureAtlas(named: "explosion")
for i in 1...textureAtlas.textureNames.count {
let name = "explosion\(i).png"
textureArray.append(SKTexture(imageNamed: name))
print(i)
}
explosion = SKSpriteNode(imageNamed: "explosion1.png" )
explosion.setScale(0.6)
explosion.position = spawnPosition
explosion.zPosition = 3
self.addChild(explosion)
print(textureAtlas.textureNames.count)
//explosion animation-action
let explosionAnimation = SKAction.repeat(SKAction.animate(with: textureArray, timePerFrame: 0.05), count: 1)
let delete = SKAction.removeFromParent()
let explosionSequence = SKAction.sequence([explosionSound, explosionAnimation, delete])
explosion.run(explosionSequence)
}
The expected result is, when the function is called, the animation should run through ONCE and delete itself. Instead, it runs once up to 10 or so times.
Thanks to #Knight0fDragon, I was able to fix this issue by making the texture array local within the function. Now each explosion has it's own instance.
func spawnExplosion(spawnPosition: CGPoint) {
var explosion = SKSpriteNode()
var textureAtlas = SKTextureAtlas()
var textureArray = [SKTexture]()
textureAtlas = SKTextureAtlas(named: "explosion")
for i in 1...textureAtlas.textureNames.count {
let name = "explosion\(i).png"
textureArray.append(SKTexture(imageNamed: name))
}
explosion = SKSpriteNode(imageNamed: "explosion1.png" )
explosion.setScale(0.6)
explosion.position = spawnPosition
explosion.zPosition = 3
self.addChild(explosion)
//explosion animation-action
let explosionAnimation = SKAction.repeat(SKAction.animate(with: textureArray, timePerFrame: 0.05), count: 1)
let delete = SKAction.removeFromParent()
let explosionSequence = SKAction.sequence([explosionSound, explosionAnimation, delete])
explosion.run(explosionSequence)
}
Has anyone managed to get an SCNText string wrapping correctly within a containerFrame in ARKit?
I've had a go, but the lines seem to be superimposed on top of each other, rather than being rendered vertically in sequence. If it was a problem with the size of the containerFrame being too small, I'd expect the string to just be truncated. It doesn't make a difference which truncation mode I use (...end / ...none / ..middle) etc.
This is code from my SCNNode subclass, creating the extruded text in the init method. The same code works fine (with different sizes obviously) to produce wrapped, extruded text in a standard SceneKit view.
let extrudedText = SCNText(string: definition.text, extrusionDepth: 0.1)
extrudedText.font = UIFont(name: definition.fontname, size: 0.2)!
extrudedText.containerFrame = CGRect(origin: .zero, size: CGSize(width: 1.8, height: 1.5))
extrudedText.truncationMode = kCATruncationMiddle
extrudedText.isWrapped = true
extrudedText.alignmentMode = kCAAlignmentLeft
let material = SCNMaterial.material(named: "rustediron-streaks")
extrudedText.materials = [material]
geometry = extrudedText
// Update pivot of object to its center
// https://stackoverflow.com/questions/44828764/arkit-placing-an-scntext-at-a-particular-point-in-front-of-the-camera
let (min, max) = boundingBox
let dx = min.x + 0.5 * (max.x - min.x)
let dy = min.y + 0.5 * (max.y - min.y)
let dz = min.z + 0.5 * (max.z - min.z)
pivot = SCNMatrix4MakeTranslation(dx, dy, dz)
Answer from Apple: my font size was too small. If I use a "normal" font size and containing frame on the SCNText object, and then set a scale on the node that contains it, everything wraps as expected. Something like:
extrudedText.font = UIFont(name: definition.fontname, size: 20)!
extrudedText.containerFrame = CGRect(origin: .zero, size: CGSize(width: 100.0, height: 500.0))
...
scale = SCNVector3Make(0.01, 0.01, 0.01)
I've been looking for a little while on how to create a circle in Swift, but it seems quite complicated. I want to create a circle at run time, with a specified x and y, and width and height. I then want to add this circle to an array where I can create more circles and add to it.
How do I do this?
Edit: This is what I've tried so far:
var center : CGPoint = touchLocation
var myContext : CGContextRef = UIGraphicsGetCurrentContext()
let color : [CGFloat] = [0, 0, 1, 0.5]
CGContextSetStrokeColor (myContext, color);
CGContextStrokeEllipseInRect (myContext, CGRectMake(touchLocation.x, touchLocation.y, 20, 20));
touchLocation is the location of the users finger. This crashes on execution on this line:
var myContext : CGContextRef = UIGraphicsGetCurrentContext()
The error says "Unexpectedly found nil while unwrapping an Optional value
Also, this doesn't allow me to add the circle to an array, because I don't know what variable type it is.
There are many ways to draw a circle, here is a snippet that I have been hacking with:
func circleWithCenter(c:CGPoint, radius r:CGFloat,
strokeColor sc: UIColor = UIColor.blackColor(),
fillColor fc: UIColor = UIColor.clearColor()) -> CAShapeLayer {
var circle = CAShapeLayer()
circle.frame = CGRect(center:c, size:CGSize(width:2*r, height:2*r))
circle.path = UIBezierPath(ovalInRect:circle.bounds).CGPath
circle.fillColor = fc.CGColor
circle.strokeColor = sc.CGColor
circle.fillColor = fc == UIColor.clearColor() ? nil : fc.CGColor
return circle
}
Note that I extended CGRect (using Swift specific features) to add a initializer that takes a center, but that is not material to your question.
Your code is not "creating" a circle as an object, it is attempting to draw one in the graphics context. What you need to do is to create a bezier path object, draw into that and save the path in your array. Something like:
var circleArray = [CGMutablePathRef]()
// ...
let path: CGMutablePathRef = CGPathCreateMutable()
CGPathAddArc(path, nil, touchLocation.x, touchLocation.y, radius, 0, M_PI * 2, true)
circleArray += [path]
You then need to stroke the path(s) in your draw routine.
With the help of a fellow stackoverflow user, I am able to change the position of a two lines and a circle on the stage using the following:
var circle2 = new Kinetic.Circle({
drawFunc: function(canvas) {
var context2 = canvas.getContext();
var centerX2 = blueLine2.getPosition().x;
centerY2 = greenLine2.getPosition().y;
context2.drawImage(gArrow2, -156, -23 + centerY2, 11, 23);
context2.drawImage(gArrow2, 156, -23 + centerY2, 11, 23);
context2.drawImage(bArrow2, centerX2, 156, 23, 11);
context2.drawImage(bArrow2, centerX2, -156, 23, 11);
context2.beginPath();
context2.arc(centerX2, centerY2, this.getRadius(), 0, 2 * Math.PI, false);
context2.lineWidth = this.getStrokeWidth();
context2.strokeStyle = this.getStroke();
context2.stroke();
},
x: cx + gx,
y: cy + gy,
radius: 70,
stroke: '#00ffff',
strokeWidth: 3,
opacity: 0.5
});
layer2.add(circle2);
It works great, now my challenge is if I move a line on a second stage for example the horizontal line, I can also move the horizonal line on the first stage using:
greenLine2.on('dragend', function (event) {
var y1 = greenLine2.getPosition().y;
greenLine3.setPoints([0, 256 + y1, 512, 256 + y1]);
centerY3 = 256 + y1;
layer3.draw();
layer2.draw();
});
However I can't update the layer to move also the vertical line and the circle. I would appreciate your suggestions, thanks in advance.
Lets say that greenLine2 is the one you're moving, and you want greenLine3 to move to the same position on the other stage. I'm going to assume the stages are the same size, but you can change up the code to account for these changes.
greenLine2.on('dragmove', function (event) {
var userPos = stage.getUserPosition(); //if this doesnt work the way you need, try a different approach, such as below:
//var userPos = greenLine.getPosition(); //other possibility
greenLine3.setPosition(userPos);
layer3.draw();
layer2.draw();
});
and if you want other things to move as well, you can do the same kind of code using .setPosition() with some offset so that the drawing is relative.
Another approach would be to create a group in each stage, and make the group draggable, that way, you can drag all the items in a group at the same time, and synchronously across stages.