I have been running around in circles
I use the Web Animation API transform:translate()
to make my player run around the bases.
runto("first").then(() => runto("second"));
does the correct animation,
but then deletes the first/previous translate
to first base,
thus my player ends up in the wrong location
I have tried every combination of
- https://developer.mozilla.org/en-US/docs/Web/API/KeyframeEffect/composite
- https://developer.mozilla.org/en-US/docs/Web/API/KeyframeEffect/iterationComposite
Is it at all possible to preserve all the translates on one element?
JSFiddle playground: https://jsfiddle.net/WebComponents/8df2qsop/
let field = document.getElementById("FIELD");
let player = field.getElementById("player");
function getCenter(el , { left, top, width, height } = el.getBoundingClientRect()) {
let point = Object.assign(field.createSVGPoint(),{ x : left width / 2, y : top height / 2 }).matrixTransform(field.getScreenCTM().inverse());
return { cx:~~point.x, cy:~~point.y };
}
function runto(destination) {
let {cx:playerX,cy:playerY} = getCenter(player);
let {cx:baseX ,cy:baseY } = getCenter(field.getElementById(destination));
let translateX = baseX - playerX;
let translateY = baseY- playerY;
let track = `<line x1="${playerX}" y1="${playerY}" x2="${baseX}" y2="${baseY}" stroke-width="10" stroke="black"/>`;
field.insertAdjacentHTML("beforeend",track);
return player.animate([{
//transform: `translate(${0}px,${0}px)`
}, { transform: `translate(${translateX}px,${translateY}px)` }],
{ duration:500, fill:"forwards", composite:"add" }
).finished; // return Promise
}
runto("first").then(() => runto("second"));
<style>
#FIELD { background: lightgreen; width: 150px; margin: 20px }
</style>
<svg id=FIELD viewBox="0 0 250 250" transform="rotate(45)">
<g fill="brown">
<path id="home" d="M 150 150 h 80 v 80 h -80 v -80z" fill="green"/>
<path id="first" d="M 150 20 h 80 v 80 h -80 v -80z"/>
<path id="second" d="M 20 20 h 80 v 80 h -80 v -80z"/>
<path id="third" d="M 20 150 h 80 v 80 h -80 v -80z"/>
</g>
<circle id="player" cx="190" cy="190" r="30" fill="gold"/>
</svg>
CodePudding user response:
You could call Animation#commitStyles()
after the animation completes, so that its state is "written" to your player.
You now need to come back to the default composite: "replace"
option, and you also need to modify your initial player settings so that it's positioned using CSS translate()
instead of using its cx
and cy
values (or you could also modify your calculations so they return relative positions instead).
let field = document.getElementById("FIELD");
let player = field.getElementById("player");
function getCenter(el, { left, top, width, height } = el.getBoundingClientRect()) {
let point = Object.assign(field.createSVGPoint(), {
x: left width / 2,
y: top height / 2
}).matrixTransform(field.getScreenCTM().inverse());
return {
cx: ~~point.x,
cy: ~~point.y
};
}
async function runto(destination) {
let { cx: playerX, cy: playerY } = getCenter(player);
let { cx: baseX, cy: baseY } = getCenter(field.getElementById(destination));
let translateX = baseX - playerX;
let translateY = baseY - playerY;
let track = `<line x1="${playerX}" y1="${playerY}" x2="${baseX}" y2="${baseY}" stroke-width="10" stroke="black"/>`;
field.insertAdjacentHTML("beforeend", track);
const anim = player.animate([{
transform: `translate(${baseX}px,${baseY}px)`
}], {
duration: 500,
fill: "forwards"
});
await anim.finished;
anim.commitStyles(); // write the current state to the animated element
anim.cancel(); // no need to keep it around anymore
}
runto("first").then(() => runto("second"));
<style>
#FIELD {
background: lightgreen;
width: 150px;
margin: 20px
}
</style>
<svg id=FIELD viewBox="0 0 250 250" transform="rotate(45)">
<g fill="brown">
<path id="home" d="M 150 150 h 80 v 80 h -80 v -80z" fill="green"/>
<path id="first" d="M 150 20 h 80 v 80 h -80 v -80z"/>
<path id="second" d="M 20 20 h 80 v 80 h -80 v -80z"/>
<path id="third" d="M 20 150 h 80 v 80 h -80 v -80z"/>
</g>
<circle id="player" cx="0" cy="0" r="30" style="transform:translate(190px, 190px)" fill="gold"/>
</svg>