#css #css3 #svg #css_animation #svg_animation
Необходимо анимировать для веб странички Диаграмму процесса дизайн-мышления
Сценарий анимации:
Движение кривых, показывающих процесс прохождения по этапам диаграммы
Во время выполнения одного этапа крутится лоадер
По окончанию выполнения одного этапа лоадер постепенно исчезает
Появляется зелёный чекбокс, сигнализирующий о завершении этапа
Начинается выполнении второго этапа и так далее до завершения
последнего этапа
У меня есть лоадер с тремя сегментами
circle {
fill:none;
stroke:#777777;
}
Взят отсюда: https://ru.stackoverflow.com/a/944006/28748
Анимацию линий со стрелкой можно реализовать, как сделано в этом топике:
https://ru.stackoverflow.com/a/955002/28748
Вопрос: Как это всё собрать вместе, рассчитать тайминги анимаций, чтобы выполнить
сценарий?
Ответы
Ответ 1
Вот потрудился над d3 вариантом (пока что не все анимации). Тут все рассчитывается в рантайме, анимации синхронизированны программно и запускаются, когда стрелочка достаточно приближается к блокам, удаляется от блоков. Фокусы d3, которыe я тут использовал: 1. d3.line().curve() let line = d3.line().curve(d3.curveCardinal); Этот вызов возвращает функцию-интерполятор, которая принимает на вход массив точек, из которых по выбранному алгоритму (здесь d3.curveCardinal) возвращает d для path. 2. selection.transition().attrTween(...) arrow.transition(t).attrTween("transform", function(){return ...}); Позволяет задать правило для интерполяции строкового css параметра, такого, как color или transform. 3. d3.interpolateString() Собственно, интерполятор между строковыми значениями. Вызов возвращает функцию, которая производит интерполяцию между строковыми значениями. Вот результат, нажмите на чекбокс, чтобы отредактировать кривую: let w = 115; let editPath = d3.select('path.edit-path'); let paths = d3.selectAll('path.path'); let zones = d3.select('g.zones').selectAll('g') .data(d3.range(6).map(d => ({i: d, x:135 + d*w}))) .enter().append('g') .attr('transform', d => `translate(${d.x}, 0)`); let circles = zones.append('circle').classed('loader', true) .attr('r', 20).attr('cx', 60).attr('cy', 170); let marks = zones.append('path').attr('stroke', 'transparent') .attr('fill', 'none').attr('stroke-width', 4) .attr('d', "M40,165 l15,20 l20,-30"); let rects = zones.append('rect').attr('width', w).attr('height', 480); let arrow = d3.select(".arrow"); let pathNode = editPath.node(); let totalLength = pathNode.getTotalLength(); d3.select('input#modify').on('change', () => { let editMode = d3.select('input#modify').node().checked; paths.classed('no-dasharray', editMode); rects.classed('no-dasharray', editMode); d3.selectAll('circle.knob').classed('no-dasharray', editMode); }); editor(editPath); anim(); function anim() { paths.attr('d', editPath.attr('d')); draw(true); function draw(now) { var t = d3.transition() .duration(16000) .delay(now ? 0 : 1000) .on("start", function() { d3.selectAll("path").style("display", "block"); marks.attr('stroke','transparent') circles.attr('stroke', 'transparent') .each(d => {d.started = d.done = d.checked = false}); }) .on("end", draw); arrow.transition(t).attrTween("transform", function() { return function(t) { let pos = t * totalLength; let pt = pointAtLength(pos); circles.each(function(d) { let dx = Math.abs(d.x - pt[0] + w/2); if (!d.started && dx < w/2) { d.started = true; d3.select(this) .transition(100).attr('stroke', 'black'); } if (!d.done && d.started && dx > w/2) { d.done = true; d3.select(this).transition(300).attr('stroke','transparent') } }) marks.each(function(d) { if (!d.checked && d.done) { d.checked = true; d3.select(this).transition(300).attr('stroke','green') } }) return `translate(${pt}) rotate(${tangentAt(pos)})`; }; }); paths.transition(t).attrTween("stroke-dasharray", function() { return d3.interpolateString("0," + totalLength, totalLength + "," + totalLength); }); circles.transition(t).attrTween("stroke-dashoffset", function() { return d3.interpolate(0, 1000); }); } function pointAtLength(l) { let xy = pathNode.getPointAtLength(l); return [xy.x, xy.y]; } function tangentAt(l) { let a = pointAtLength(Math.max(l - 0.01, 0)), b = pointAtLength(l + 0.01); return Math.atan2(b[1] - a[1], b[0] - a[0]) * 180 / Math.PI; } } function editor(target) { let size = 800; let points = d3.range(0, 13).map(function(i) { return [135 + i*w/2, 250-100*Math.random()]; }); let dragged = null, selected = points[points.length-1]; let line = d3.line().curve(d3.curveCardinal); let svg = d3.select("svg"); svg.append("rect") .attr("width", size) .attr("height", size); d3.select(window) .on("mousemove", mousemove) .on("mouseup", mouseup); redraw(); function redraw() { paths.datum(points).attr("d", line); totalLength = pathNode.getTotalLength(); var circle = svg.selectAll("circle.knob") .data(points, d => d); circle.exit().remove(); let newNodes = circle.enter() .append("circle").classed('knob', true) .attr("r", 1e-6) .on("mousedown", d => { selected = dragged = d; redraw(); }) .transition() .duration(250) .attr("r", 6.5); circle.merge(newNodes) .classed("selected", d => d === selected) .attr("cx", d => d[0]) .attr("cy", d => d[1]); if (d3.event) { d3.event.preventDefault(); d3.event.stopPropagation(); } } function mousemove() { if (!dragged) return; let m = d3.mouse(svg.node()); dragged[0] = m[0]; dragged[1] = m[1]; redraw(); } function mouseup() { if (!dragged) return; mousemove(); dragged = null; } } rect { fill: none; } circle.loader { fill: none; stroke-width: 2.2px; stroke-dasharray: 32 10; } path.path { fill: none; stroke: red; stroke-width:2.2; } circle { fill: transparent; cursor: move; } .no-dasharray { stroke-dasharray: 0 !important; stroke: red; } modifyОтвет 2
Движение кривых, показывающих процесс прохождения по этапам диаграммы Загружаем картинку в векторный редактор и с помощью инструмента - Рисовать кривые Безье, повторяем траекторию кривых. Сохраняем патчи кривых в другой файл и реализуем анимацию роста кривых с помощью stroke-dashoffset. При уменьшении этого атрибута от 1192px до нуля, линия будет расти от нуля до максимума. .shape svg { width:100%; height: auto; } #outline1, #outline2 { fill:none; stroke-dasharray: 1192; animation: dash 17s linear forwards; stroke-linejoin: round; stroke-width:4; } @keyframes dash { from { stroke-dashoffset: 1192; } to { stroke-dashoffset: 0; } } .container { width:100%; height:100%; background-image: url("https://www.transparenttextures.com/patterns/3px-tile.png"); background: linear-gradient(to right,#B452A0, #895FDD ); display: flex; justify-content: center; align-items:center; }Все дальнейшие пояснения даны в комментариях кода. Тайминг последовательных и параллельных анимаций осуществляется цепочками условий основанных на привязке к id анимаций. Другими словами, begin="an_check1.end-0.5s" - данная анимация начнется, когда закончится анимация с id="an_check1" минус пол секунды. .shape svg { width:100%; height: auto; } #outline1, #outline2 { fill:none; stroke-dasharray: 1192; animation: dash 17s linear forwards; stroke-linejoin: round; stroke-width:4; } @keyframes dash { from { stroke-dashoffset: 1192; } to { stroke-dashoffset: 0; } } .container { width:100%; height:100%; background: linear-gradient(to right,#B452A0, #895FDD ); display: flex; justify-content: center; align-items:center; }Приложение полностью адаптивно. Работает во всех современных браузерах. Анимация не работает в IE и Edge, так как Microsoft не хочет поддерживать анимацию SVG. Да и многое другое, в том числе современные технологии CSS3, плохо или совсем не поддерживаются браузерами Microsoft.
Комментариев нет:
Отправить комментарий