我想创建一个简单的svg动画库。我希望能够在javascript中定义关键帧,然后该库将使用stroke-dasharray和dashoffset属性上的过渡为svg路径设置动画。奇怪的是,它第一次正常工作。再次触发时,仅#path1会正确设置动画。我尝试在chrome中调试(也在chrome中测试),当我使用调试器时,它可以工作。我检查了每个变量,它们都是正确的。
出于测试目的,我将添加所有文件和一个指向jquery的外部链接。
var JAnim = {
parse_distance: function(val, total_distance){
// val is already a number
if(typeof val == "number"){
return val;
}
var parsed = parseInt(val, 10);
// invalid value given
if(isNaN(parsed)){
throw "Value " + val + " is not a valid number!";
}
// number given in percent
if(val[-1] == "%"){
return (parsed / 100) * total_distance;
}
// number given as a string
return parsed;
},
set: function(target, options) {
// check if values were supplied
if(options == undefined || options == null){
throw "No options specified!";
}
if(options.begin == undefined || options.begin == null){
throw "Missing mandatory begin attribute!";
}
if(options.end == undefined || options.end == null){
throw "Missing mandatory end attribute!";
}
// --------------------------------------------------------
// query target elements
var elements = document.querySelectorAll(target);
for(var i = 0; i < elements.length; i++){
// check if valid element is selected
if(elements[i].tagName != "path"){
throw "Unsupported element: \"" + elements[i].tagName + "\" !";
}
var total_distance = elements[i].getTotalLength();
var begin = this.parse_distance(options.begin, total_distance);
var end = this.parse_distance(options.end, total_distance);
// sanity check
if(begin > end) {
throw "Begin value greater than end!";
}
var distance_to_show = end - begin;
// stroke-dasharray formula: distance_to_show remaining_distance_to_hide
// stroke-dashoffset uses negative begin value, because positive values offset from the back
elements[i].style["stroke-dasharray"] = distance_to_show.toString() + " " + (total_distance - distance_to_show).toString();
elements[i].style["stroke-dashoffset"] = (-begin).toString();
}
},
animate: function(target, animation){
// check if proper values were supplied
if(animation == undefined || animation == null){
throw "No animation specified!";
}
if(animation.duration == undefined || animation.duration == null){
throw "Missing mandatory duration attribute!";
}
if(typeof animation.duration != "number"){
throw "Invalid value given from duration!";
}
if(document.querySelectorAll(target).length == 0){
// no items were selected
return;
}
// get percentages and sort them
var keys = Object.keys(animation.keyframes);
keys.sort(function(a,b) { return parseInt(a) - parseInt(b); });
this.clear_transition(target);
// set initial state
var i = 0;
if(keys[i] == "0%"){
this.set(target, {begin: animation.keyframes[keys[i]].begin, end: animation.keyframes[keys[i]].end });
i++;
}
// calculate time
var time = (parseInt(keys[i]) / 100) * animation.duration;
this.set_transition(target, "stroke-dasharray " + time + "ms " + animation.keyframes[keys[i]].timing + ", " +
"stroke-dashoffset " + time + "ms " + animation.keyframes[keys[i]].timing);
this.set(target, {begin: animation.keyframes[keys[i]].begin, end: animation.keyframes[keys[i]].end });
i++;
},
clear_transition: function(target){
var elements = document.querySelectorAll(target);
for(var i = 0; i < elements.length; i++){
elements[i].style["transition-property"] = "none";
}
},
set_transition: function(target, trans){
var elements = document.querySelectorAll(target);
for(var i = 0; i < elements.length; i++){
elements[i].style.transition = trans;
}
}
}
// -------------------------------------- lib ends here ------- the next part is just using it
var menu_anim_id = null;
function animate_menu(){
var anim = {
duration: 2500,
keyframes: {
"0%": {
begin: 0,
end: 80
},
"50%": {
begin: 80,
end: 81,
timing: "ease-in"
},
"100%": {
begin: 95,
end: 167,
timing: "ease-out"
}
}
};
JAnim.animate("#path1, #path3", anim);
/*
anim = {
duration: 500,
on_finish: null,
keyframes: {
"0%": {
begin: 0,
end: 80
},
"100%": {
begin: 40,
end: 40,
timing: "ease-out"
}
}
};
JAnim.animate("#path2", anim);
*/
}
function keypressed(e) {
if(e.key == "Enter"){
JAnim.set("#path1, #path3", {begin: 0, end: 80});
}
if(e.key == " ") {
JAnim.set("#path1, #path3", {begin: 20, end: 30});
}
}
function setup(){
JAnim.set("#path1, #path3", {begin: 0, end: 80});
$("#btn").click(animate_menu);
$(document).keypress(keypressed);
}
$(document).ready(setup);
* {
margin: 0;
}
#menu path {
fill: none;
stroke: black;
stroke-width: 15px;
stroke-linecap: round;
stroke-linejoin: round;
}
#btn {
width: 70pt;
height: 70pt;
position: fixed;
top: calc(50% - 70pt);
left: calc(50% - 70pt);
}
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Title</title>
<link rel="stylesheet" type="text/css" href="res/css/main.css" />
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
</head>
<body>
<div id="btn">
<svg id="menu" viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg" xmlns:svg="http://www.w3.org/2000/svg">
<path id="path1" d="m10,20 l80,0 l-70,50"/>
<path id="path2" d="m10,50 l80,0"/>
<path id="path3" d="m10,80 l80,0 l-70,-50"/>
</svg>
</div>
</body>
</html>
我对webdev还是很陌生,不确定这是否是正确的方法。问题最有可能出现在动画功能中。对我来说,过渡似乎没有正确更新。如果您是第一次运行动画,那么请按空格键使其进入“测试状态”,然后再次触发动画。结果是#path1捕捉到正确的状态并按照定义进行动画处理,但是#path3只是异常地过渡到了该状态。同时按回车键将其恢复为原始状态,然后再次运行。请随时询问更多信息,谢谢。