【发布时间】:2018-02-28 17:34:04
【问题描述】:
我正在使用 svg.js 创建一个骑自行车的人的动画。半完整版在这里:https://pedalfuriously.neocities.org/。在使用 requestAnimationFrame(而不是内置动画的 svg.js)创建动画期间,我遇到了移动和旋转 svg 元素的问题。
如果你看一下链接,用踏频滑块让骑手的踏板速度非常快,然后将滑块快速翻转到零,你可以看到他的小腿在一个断开的方式。真正让我头疼的是,每帧中腿的位置是根据与曲柄旋转的绝对关系确定的(而不是采用一些增量时间值来确定该帧上的运动)。
我想我已经能够确认我的代码的哪个方面导致了问题。这是一个没有表现出确切行为的最小示例,但我认为说明了我认为应该负责的那种事情:
var draw = SVG("drawing").viewbox(0, 0, 400, 400)
var origin = {
x: 70,
y: 70
}
var length = 60
var blueLine = draw.group()
blueLine.line(0, 0, 0 + length, 0).move(origin.x, origin.y)
.stroke({
color: "#00f",
width: 4
})
blueLine.angle = 0
var greenLine = draw.group()
greenLine.line(0, 0, 0 + length, 0).move(origin.x, origin.y)
.stroke({
color: "#0f0",
width: 4
})
greenLine.angle = 0
var previous = 0
var dt = 0
var step = function(timestamp) {
dt = timestamp - previous
previous = timestamp
blueLine.angle += 0.18 * dt
blueLine.rotate(blueLine.angle, origin.x, origin.y)
var endX = Math.cos(toRad(blueLine.angle)) * length
var endY = Math.sin(toRad(blueLine.angle)) * length
// Comment out this line, and rotation works fine
greenLine.move(endX, endY)
greenLine.angle = blueLine.angle - 10
// Comment out this line, and movement works fine
greenLine.rotate(greenLine.angle, origin.x, origin.y)
// But they don't work together. If I both move and rotate
// the green line, it goes in this crazy huge arc, rather
// than rotating neatly around the end of the blue line
// as expected.
window.requestAnimationFrame(step)
}
window.requestAnimationFrame(step)
function toRad(deg) {
return deg * (Math.PI / 180)
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/svg.js/2.6.4/svg.js"></script>
<div id="drawing"></div>
我在实际代码中注意到的其他一点是,如果我移动腿的位置,它会改变问题的严重性,甚至完全停止它。如果臀部一直位于自行车前部附近,问题就没有那么严重了。此外,如果我禁用小腿的旋转,则不会出现抖动。在某些位置,即使在任何动作开始之前,小腿也会在加载时立即旋转出屏幕。
我希望得到一些指导,说明我是否误解了操作元素的工作方式,尤其是在 svg.js 中,或者在一般的 SVG 中。
感谢善良的矢量图形专家!
这是腿的实际代码。 step() 函数可能是最相关的。不确定它是否会有所帮助:
Rider.Leg = function(foot, front, xOffset, yOffset) {
var upper = front ? SVGE.upperLeg : SVGE.upperLegBack
var lower = front ? SVGE.lowerLeg : SVGE.lowerLegBack
this.foot = foot
this.draw = foot.draw
this.geo = {
upper: {
x: this.foot.pedal.gear.x + 150,
y: this.foot.pedal.gear.y - 750,
length: 396
},
lower: {
length: 390
}
}
this.upper = this.draw.group().svg(upper).move(this.geo.upper.x, this.geo.upper.y)
.transform({ scale: 0.95, cx: 0, cy: 0 })
this.lower = this.draw.group().svg(lower).move(this.geo.upper.x, this.geo.upper.y)
}
// Step function does not take in a time argument. Positioning of legs is based only on
// the absolute position of other elements, none of which jiggle.
Rider.Leg.prototype.step = function () {
var angle = this.pedalAngle() - Math.PI
var ha = this.scaleneAngle(this.geo.lower.length, this.geo.upper.length, this.pedalDistance())
var ka = this.scaleneAngle(this.pedalDistance(), this.geo.lower.length, this.geo.upper.length)
var x = this.geo.upper.length * Math.cos(ha + angle)
var y = this.geo.upper.length * Math.sin(ha + angle)
this.upper.rotate(Drive.toDeg(angle + ha), 0, 0)
this.lower.move(this.geo.upper.x + x, + this.geo.upper.y + y)
this.lower.rotate(Drive.toDeg(angle + ha + ka - Math.PI), 0, 0)
}
// Gets the distance between the hip joint and the pedal
Rider.Leg.prototype.pedalDistance = function () {
var pos = this.foot.getPos()
var xDist = this.geo.upper.x - pos.x
var yDist = this.geo.upper.y - pos.y
return Math.hypot(xDist, yDist)
}
// Gets the angle between the hip joint and the pedal
Rider.Leg.prototype.pedalAngle = function () {
var pos = this.foot.getPos()
var xDist = this.geo.upper.x - pos.x
var yDist = this.geo.upper.y - pos.y
return Math.atan2(yDist, xDist)
}
Rider.Leg.prototype.scaleneAngle = function (a, b, c) {
return Math.acos(((b * b) + (c * c) - (a * a)) / (2 * b * c))
}
【问题讨论】:
-
一些观察:如果你查看开发工具,你可以看到小腿变换矩阵在速度降低到 0 后一直在变化。变化很小(@987654325 的第 7 位或第 8 位有效数字@)。这种效果持续多长时间不同。
-matrix.b = matrix.c越接近 -1,它就会持续振荡最长(甚至无限期)。由于变换基本上是旋转,这意味着matrix.c = sin(α) ≈ -1,α 是您设置为.rotate()的参数的值。请注意,matrix.a = cos(α) > 0.1 at all times, soα ≈ -80°` 最大。 -
应用旋转时,首先使用
untransform()重置转换。绝对转换确实不精确,所以每次都重新开始会更好更快 -
另外:您在组中使用
move()。组只能通过翻译移动。正如我上面所说的,绝对变换是不精确的。所以最好相对:greenLine.transform({x:endX, y:endY, relative: true}) -
@ccprog @Fuzzyma - 谢谢你们。我用对
transform()的调用替换了对move()和rotate的调用,并且抖动似乎已经解决了。解决腿部抖动后,我注意到一个更微妙的脚抖动,我以类似的方式解决了。 -
很好,我会回答的
标签: javascript animation svg svg.js