你好,我想用JavaScript写一个冒泡排序的动画,具体样式可参考VisuAlgo.net。
写的时候动画用的SVG。然而实际执行过程中发现SVG的动画似乎和JavaScript的线程不是同一个线程。因此我的demo里面改变选中g label的语句在AnimateMotion动画结束之前就执行了,达不到我想要的效果。请教一下该如何解决?
代码粘贴如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>BubbleSort</title>
<style>
text {
fill: black;
font: 20px sans-serif;
text-anchor: middle;
}
#sort-viz {
width: 100%;
text-align: center;
overflow: hidden;
padding-top: 10px;
}
</style>
<!-- <script type="text/javascript">-->
<!-- function display_alert()-->
<!-- {-->
<!-- console.log("I am an alert box!!")-->
<!-- }-->
<!-- </script>-->
</head>
<body>
<div id="sort-viz" >
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" id="sort-svg" height="490" width="1000" >
<!-- <g >-->
<!-- <rect x="100" y="100" width="100" height="500" fill="red" fill-opacity="0.8">-->
<!-- <animateTransform id="pause" dur="3s" type="translate" attributeType="XML" attributeName="transform"/>-->
<!-- <animateMotion id="red" begin="pause.end" attributeName="x" from="100,0" to="500,0" dur="3s" fill="freeze" />-->
<!-- </rect>-->
<!-- <text dy=".35em" x="50" y="450" >50</text>-->
<!-- </g>-->
<!-- <g>-->
<!-- <rect x="400" y="100" width="100" height="500" fill="green">-->
<!-- <animate id="green" begin="red.end" onend="red.beginElement()" attributeName="x" from="500" to="100" dur="3s" fill="freeze" />-->
<!-- </rect>-->
<!-- </g>-->
</svg>
</div>
</body>
<script>
class Randomize{
Array1D(N){
let arr=new Array();
for(let i=0; i<N; i++){
arr.push(parseInt(Math.random()*51,10));
}
return arr;
//parseInt(Math.random()*51,10);
//Math.floor(Math.random()*51);
}
}
let randomizer=new Randomize();
let arr=randomizer.Array1D(15);
/*class LogTracer {
LogTracer() {
}
set() {
}
print(message) {
}
}*/
class ChartItem{
g=document.createElementNS('http://www.w3.org/2000/svg', 'g');
rect=document.createElementNS('http://www.w3.org/2000/svg', 'rect');
text=document.createElementNS('http://www.w3.org/2000/svg', 'text');
Animate=document.createElementNS('http://www.w3.org/2000/svg','animateMotion');
isMotion=false;
CharItem(key, value){
this.setPosition(key, value);
this.initRect();
this.setHeight(value);
this.initText();
this.text.setAttribute('y', 5*value-15);
this.setContent(value);
this.g.appendChild(this.rect);
this.g.appendChild(this.text);
}
initRect(){
this.rect.setAttribute('width', '45');
this.setFillDeactivate();
}
initText(){
this.text.setAttribute('dy', '.35em');
this.text.setAttribute('x', '22.5');
}
initAnimate(key){
this.Animate.setAttribute('attributeName', 'x')
this.Animate.setAttribute('from', '0,0');
this.Animate.setAttribute('to', (50*key).toString()+',0');
this.Animate.setAttribute('dur', '3s');
this.Animate.setAttribute('fill', 'freeze');
}
setFillDeactivate(){
if(!this.isMotion){
this.rect.setAttribute('fill', 'rgb(173,216,230)');
}
}
setFillActivate(){
if(!this.isMotion){
this.rect.setAttribute('fill', 'rgb(0, 128, 0)');
}
}
removeAnimate(){
this.g.removeChild(this.Animate);
}
setMotion(key){
this.isMotion=true;
this.initAnimate(key);
this.g.appendChild(this.Animate);
this.setXPosition(key);
}
setHeight(value){
this.rect.setAttribute('height', 5*value);
}
setPosition(key, value){
this.g.setAttribute('transform', 'translate('+50*key+','+(250-5*value)+')');
}
setXPosition(key){
let str=this.g.getAttribute('transform');
let x=str.substring(str.indexOf('(')+1, str.indexOf(','));
let y=str.substring(str.indexOf(',')+1, str.indexOf(')'));
x=parseInt(x)+50*key;
y=parseInt(y);
this.Animate.onend= () => {
this.isMotion=false;
this.removeAnimate();
this.g.setAttribute('transform', 'translate('+x+','+y+')');
}
}
setContent(value){
this.text.textContent=value;
}
}
class ChartTracer{
svg=document.getElementById("sort-svg");
ChartItems=[];
set(array){
for(let i=0; i<array.length; i++){
let item=new ChartItem();
item.CharItem(i, array[i]);
this.ChartItems.push(item);
this.svg.append(item.g);
}
}
// reset(){
//
// }
//
// delay(){
//
// }
//
swap(index1, index2){
// var temp=this.ChartItems[index1];
// this.ChartItems[index1]=this.ChartItems[index2];
// this.ChartItems[index2]=temp;
this.ChartItems[index1].setMotion(index2-index1);
this.ChartItems[index2].setMotion(index1-index2);
}
select(index){
this.ChartItems[index].setFillActivate();
}
deselect(index){
this.ChartItems[index].setFillDeactivate();
}
//
// chart(){
//
// }
}
let chartTracer=new ChartTracer();
chartTracer.set(arr);
chartTracer.select(8);
chartTracer.select(9);
chartTracer.swap(8,9);
chartTracer.deselect(8);
chartTracer.deselect(9);
/*class Array1DTracer{
init(){
}
set(){
}
patch(){
}
depatch(){
}
select(){
}
deselect(){
}
chart(){
}
syncChartTracer(){
}
}*/
i=3
console.log("original array = ["+arr+"]")
console.log("swap ("+i+") element: ["+arr[i]+"] and ("+(i+1)+") element: ["+arr[i+1]+"]")
console.log("updated array = ["+arr+"]")
//console.clear();
function BubbleSort(arr){
function sort(){
let virtualArr=[arr.slice()];
let max=arr.length;
for(let i=0; i<max; i++){
let done=true;
for(let j=0; j<max-i; j++){
if(arr[j]>arr[j+1]){
let temp=arr[j+1];
arr[j]=arr[j+1];
arr[j+1]=temp;
done=false;
virtualArr.push(arr.slice());
}
}
if (done){
break;
}
}
return virtualArr;
}
}
</script>
<!--
<script type="text/javascript">
function display_alert()
{
console.log("I am an alert box!!")
}
function change_translate() {
g.setAttribute('transform', 'translate(775, 0)');
}
var svg=document.getElementById('sort-svg');
svg.onclick=function () {
if (this.paused != true) {
this.pauseAnimations();
this.paused = true;
} else {
this.unpauseAnimations();
this.paused = false;
}
}
var g=document.createElementNS('http://www.w3.org/2000/svg', 'g');
g.setAttribute('transform', 'translate(175, 0)');
var rect=document.createElementNS('http://www.w3.org/2000/svg', 'rect');
rect.setAttribute('width', '45');
rect.setAttribute('height', '250');
rect.setAttribute('fill', 'rgb(173,216,230)');
var text=document.createElementNS('http://www.w3.org/2000/svg', 'text');
text.setAttribute('dy', '.35em');
text.setAttribute('x', '22.5');
text.setAttribute('y', '235');
text.textContent=50;
var animateM1=document.createElementNS('http://www.w3.org/2000/svg','animateMotion');
animateM1.setAttribute('id', 'rightShift')
animateM1.setAttribute('from', '0, 0');
animateM1.setAttribute('to', '600,0');
animateM1.setAttribute('dur', '5s');
animateM1.setAttribute('fill', 'freeze');
animateM1.setAttribute('onend', 'change_translate()')
var animateM2=document.createElementNS('http://www.w3.org/2000/svg','animateMotion');
animateM2.setAttribute('from', '100, 0');
animateM2.setAttribute('to', '-500,0');
animateM2.setAttribute('dur', '10s');
animateM2.setAttribute('fill', 'freeze');
animateM2.setAttribute('begin', 'rightShift.end')
// rect.setAttribute('x', '100');
// text.setAttribute('x', '122.5');
g.appendChild(rect);
g.appendChild(text);
g.appendChild(animateM1);
g.appendChild(animateM2);
svg.appendChild(g);
</script>
-->
</html>
<!--
<g >
<rect x="100" y="100" width="100" height="500" fill="red" fill-opacity="0.8">
<animate attributeName="x" from="100" to="500" dur="3s" repeatCount="indefinite" />
</rect>
<text dy=".35em" x="50" y="450" >50</text>
</g>
<g>
<rect x="400" y="100" width="100" height="500" fill="green">
<animate attributeName="x" from="500" to="100" dur="3s" repeatCount="indefinite" />
</rect>
</g>
-->
animateMotion
元素是SVGAnimationElement
,它上面有一个事件endEvent
,可以这样用:先得到这个元素:
或者用你上面的方法也行:
然后,给它绑一个事件:
参考资料:https://developer.mozilla.org...