I am trying to create an animation of a falling Tetris block using SVG, which includes translating and rotating the piece. I used individual animations for every move so they look as close to the game as possible. The falling animation works, but once i try to rotate the piece it doesn't keep the rotation and reverts to the original rotation (0) with the next move. Is there any way to make the piece stay rotated or perhaps even a better way i can implement the animation?
Thank you in advance for your help.
<svg id="svg" xmlns="http://www.w3.org/2000/svg" width="280" height="504" xmlns:xlink="http://www.w3.org/1999/xlink">
<defs>
<g id ="b_lightblue">
<rect x="20" y="20" width="20" height="20" fill="#00F0F0"/>
<polyline points="20 20, 16 16, 44 16, 40 20, 20 20" fill="#B3FBFB"/>
<polyline points="40 20, 44 16, 44 44, 40 40, 40 20" fill="#00D8D8"/>
<polyline points="40 40, 44 44, 16 44, 20 40, 40 40" fill="#007878"/>
<polyline points="20 40, 16 44, 16 16, 20 20, 20 40" fill="#00D8D8" />
</g>
<g id ="tetro_I">
<use xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="#b_lightblue" transform="translate(-16 -16)"/>
<use xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="#b_lightblue" transform="translate(12 -16)"/>
<use xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="#b_lightblue" transform="translate(40 -16)"/>
<use xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="#b_lightblue" transform="translate(68 -16)"/>
</g>
</defs>
<rect x="0" y="0" width="280" height="504" fill="#CDCEAE"/> <!--Background-->
<use id="tetro_1" xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="#tetro_I" transform="translate(84 28) rotate(0)" opacity="1"/> <!--Tetris Piece-->
<animateTransform xlink:href="#tetro_1" id="t1_move_1" attributeName="transform" type="translate" begin="0.8s" dur="0.02" from="84 28" to="84 65" fill="freeze"/>
<animateTransform xlink:href="#tetro_1" id="t1_move_2" attributeName="transform" type="translate" additive="sum" begin="t1_move_1.end+0.8s" dur="0.02" to="84 65" fill="freeze"/>
<animateTransform xlink:href="#tetro_1" id="t1_move_3" attributeName="transform" type="translate" additive="sum" begin="t1_move_2.end+0.8s" dur="0.02" to="84 93" fill="freeze"/>
<animateTransform xlink:href="#tetro_1" id="t1_rotate_1" attributeName="transform" type="rotate" additive="sum" begin="t1_move_3.end+0.2s" dur="0.001" from="0" to="90 56 0" fill="freeze"/>
<animateTransform xlink:href="#tetro_1" id="t1_move_4" attributeName="transform" type="translate" additive="sum" begin="t1_move_3.end+0.8s" dur="0.02" to="84 121" fill="freeze"/>
</svg>
If you use an animation that has a to
attribute, but no from
, the start value used is the current attribute value. For each step, the frozen
value from the previous step is removed and re-entered as the implicit from
. Therefore the additive="sum"
can have no effect. You would need to start each step with 0
and then write relative translations each time.
But your animation has some other flaws: First. a duration of 0.02s or shorter is nothing but instantaneous. Browsers do not use frame rates above 60 frames per second, which means each frame has a duration of 0.0167s. My advice would be to animate from one state to the next in discrete steps and be done with it. You can the write the animation as a sequence of values
over a list of keyTimes
.
Both translation and rotation must then be written absolutely for each step:
0 0;0 28;0 65;0 93;0 121
over a duration of 4 * 0.8s0;0;0;90 56 0;90 56 0
, delayed by 0.2 seconds, but with the same durationThe second issue has to do with the order of application of transformations: the rotation, when applied to the blocks at a time they have already moved down must either have its center of rotation also moved down, or it must be multiplied left side of the translations.
I think the simplest variant is to wrap the block in another <g>
element, apply the translation to that group and the rotation to the <use>
element inside. That way, order is preserved.
<svg id="svg" xmlns="http://www.w3.org/2000/svg" width="280" height="504" xmlns:xlink="http://www.w3.org/1999/xlink">
<defs>
<g id ="b_lightblue">
<rect x="20" y="20" width="20" height="20" fill="#00F0F0"/>
<polyline points="20 20, 16 16, 44 16, 40 20, 20 20" fill="#B3FBFB"/>
<polyline points="40 20, 44 16, 44 44, 40 40, 40 20" fill="#00D8D8"/>
<polyline points="40 40, 44 44, 16 44, 20 40, 40 40" fill="#007878"/>
<polyline points="20 40, 16 44, 16 16, 20 20, 20 40" fill="#00D8D8" />
</g>
<g id ="tetro_I">
<use xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="#b_lightblue" transform="translate(-16 -16)"/>
<use xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="#b_lightblue" transform="translate(12 -16)"/>
<use xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="#b_lightblue" transform="translate(40 -16)"/>
<use xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="#b_lightblue" transform="translate(68 -16)"/>
</g>
</defs>
<rect x="0" y="0" width="280" height="504" fill="#CDCEAE"/> <!--Background-->
<g id="tetro_move_1">
<use id="tetro_rotate_1" xlink:href="#tetro_I" transform="translate(84 0)"/>
</g> <!--Tetris Piece-->
<animateTransform xlink:href="#tetro_move_1" attributeName="transform" type="translate"
begin="0s" dur="3.2s" fill="freeze" additive="sum" calcMode="discrete"
values="0 0;0 28;0 65;0 93;0 121"
keyTimes="0;0.25;0.5;0.75;1"/>
<animateTransform xlink:href="#tetro_rotate_1" attributeName="transform" type="rotate"
begin="0.2s" dur="3.2s" fill="freeze" additive="sum" calcMode="discrete"
values="0;0;0;90 56 0;90 56 0;"
keyTimes="0;0.25;0.5;0.75;1"/>
</svg>