Анимация SVG по осям Z и X
Всем привет. Хочу анимировать полукруги, на css и js чтобы его элементы вращались в разных плоскостях. Если с z осью все понятно, то я с X не могу ничего придумать. Помогите, пожалуйста.
Ответы (1 шт):
Автор решения: zhurof
→ Ссылка
Решение на svg.
- Кольцо в горизонтальной плоскости релизовано через
<ellipse>с анимациейstroke-dashoffset. - Кольцо в профильной плоскости анимируется через
scale. Есть нюанс сtransform-originдляsvg: вроде в Safari не работает. - Вырез у фронтального и профильного колец на 1/4 сделан при помощи
clip-path
<svg width="320" height="320" version="1.1" viewBox="0 0 320 320" xmlns="http://www.w3.org/2000/svg" fill="none">
<clipPath id="myClip">
<rect x="50%" y="0" width="50%" height="100%" fill="black"/>
<rect x="0" y="50%" width="100%" height="50%" fill="black"/>
</clipPath>
<circle cx="160" cy="160" r="100" stroke="black" stroke-width="1" clip-path="url(#myClip)" style="transform-box: fill-box;transform-origin:center;">
<animateTransform attributeName="transform" type="scale" begin="0" dur="2s" values="1 1;-1 1;1 1" repeatCount="indefinite" />
</circle>
<circle cx="160" cy="160" r="80" stroke="black" stroke-width="1" clip-path="url(#myClip)">
<animateTransform attributeName="transform" type="rotate" begin="0" dur="2s" values="0 160 160;360 160 160" repeatCount="indefinite" />
</circle>
<ellipse cx="160" cy="160" rx="150" ry="42" stroke="black" stroke-width="3" stroke-dasharray="500 151.62">
<animate attributeName="stroke-dashoffset" begin="0" dur="2s" values="0;-651.62" repeatCount="indefinite" />
</ellipse>
<circle cx="160" cy="160" r="32" fill="orange" />
</svg>
Усложнённый вариант:
<svg width="320" height="320" version="1.1" viewBox="0 0 320 320" xmlns="http://www.w3.org/2000/svg" fill="none">
<defs>
<clipPath id="myClip">
<rect x="50%" y="0" width="50%" height="100%" fill="black"/>
<rect x="0" y="50%" width="100%" height="50%" fill="black"/>
</clipPath>
<!--маска для сокрытия кольца в горизонтальной плоскости-->
<mask id="xmask" maskUnits="userSpaceOnUse">
<rect width="100%" height="100%" fill="white"/>
<circle cx="160" cy="160" r="32" fill="black" />
<ellipse cx="160" cy="192" rx="150" ry="32" fill="white"/>
<!-- Верстикальный радиус ry маскировочного эллипса по высоте круга. Смещение от центра на один радиус круга вниз. Так обеспечивается максимальное покрытие возможных значений ry маскируемого эллипса. -->
</mask>
<!--Динамическая обрезка кольца в профильной плоскости по контурам центрального круга -->
<mask id="zmask" maskUnits="userSpaceOnUse">
<rect width="100%" height="100%" fill="white"/>
<circle cx="160" cy="160" r="32" fill="black">
<animate attributeName="cx" begin="0" dur="8s" values="160;60;160;260;160" repeatCount="indefinite" />
</circle>
</mask>
</defs>
<line x1="160" y1="40" x2="160" y2="60" stroke="black" />
<circle cx="160" cy="160" r="32" fill="orange" />
<circle cx="160" cy="160" r="100" stroke="black" stroke-width="1" clip-path="url(#myClip)" style="transform-box: fill-box;transform-origin:center;" mask="url(#zmask)">
<animateTransform attributeName="transform" type="scale" begin="0" dur="8s" values="1 1;-1 1;1 1" repeatCount="indefinite" />
</circle>
<circle cx="160" cy="160" r="80" stroke="black" stroke-width="1" clip-path="url(#myClip)">
<animateTransform attributeName="transform" type="rotate" begin="0" dur="8s" values="0 160 160;360 160 160" repeatCount="indefinite" />
</circle>
<ellipse cx="160" cy="160" rx="150" ry="20" stroke="black" stroke-width="3" stroke-dasharray="460 155.304" mask="url(#xmask)">
<animate attributeName="stroke-dashoffset" begin="0" dur="8s" values="0;-615.304" repeatCount="indefinite" />
<!-- 615.304 - это длина обводки эллипса. Можно вычислить js-методом domNode.getTotalLength(); -->
</ellipse>
</svg>
Предложу решение на css как более присспособленное для 3d-трансормаций и в данном случае более кроссбраузерное.
@keyframes rotate {
from {
transform: rotate(0)
}
to {
transform: rotate(360deg)
}
}
@keyframes rotateY {
from {
transform: rotate3d(0, 1, 0, 0)
}
to {
transform: rotate3d(0, 1, 0, 360deg)
}
}
.planet {
width: 200px;
height: 200px;
border-radius: 50%;
position: relative;
margin: 10px auto;
background: radial-gradient(circle at center, orange 30%, transparent 0);
}
.planet:before,
.planet:after {
content: '';
display: block;
position: absolute;
border-radius: 50%;
border: 1px solid black;
clip-path: polygon(0 50%, 50% 50%, 50% 0, 100% 0, 100% 100%, 0 100%);
}
.planet:before {
width: 80%;
height: 80%;
top: 10%;
left: 10%;
animation: rotate 2s linear infinite;
}
.planet:after {
width: 100%;
height: 100%;
top: 0;
left: 0;
animation: rotateY 2s linear infinite;
}
.planet__ring {
position: absolute;
width: 150%;
height: 150%;
left: -25%;
top: -25%;
transform: rotate3d(1, 0, 0, 70deg);
}
.planet__ring:before {
content: '';
display: block;
width: 100%;
height: 100%;
border-radius: 50%;
border: 3px solid black;
border-top-color: transparent;
animation: rotate 2s linear infinite;
}
<div class="planet">
<div class="planet__ring"></div>
</div>
