У меня есть приложение, которое реализует движение персонажей анимации вдоль заранее проложенного маршрута на карте города. Маршруты, точнее path
сделаны вручную в векторном редакторе. Вся техника создания маршрутов показана здесь.
Ниже код, реализующий данную технику и анимацию:
.container {
width:100%;
height:100%;
}
<div class="container">
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1" viewBox="0 0 800 540" >
<defs>
<path id="walk"
d="m343 268 34-10 50-9-33-86 22-14 7-21 8-3 13 18 34-25 47 65 22-19" />
<g id="Man" transform="translate(0,0) scale(1,-1)">
<path fill="none">
<animate
attributeName="d"
begin="0.1s"
dur="0.3s"
repeatCount="indefinite"
values="M-3,0 0,10 3,0 M0,10 0,16 l 4,-5 M0,16 l-4,-5 M0,16 c4,4 -4,4 0,0;
M 0,0 0,10 0,0 M0,10 0,16 l 0,-5 M0,16 l 0,-5 M0,16 c4,4 -4,4 0,0;
M-3,0 0,10 3,0 M0,10 0,16 l 4,-5 M0,16 l-4,-5 M0,16 c4,4 -4,4 0,0"/>
</path>
</g>
</defs>
<image xlink:href="https://i.stack.imgur.com/XPdWW.png" width="100%" height="100%" />
<path id="train" stroke-dasharray="312" stroke-dashoffset="312" stroke-width="2" d="M443 534 426 477 415 435 397 391 375 347 350 304 334 277 317 251" style="fill:none;stroke:violet;"/>
<text
font-size="28"
font-family="Times New Roman"
fill="#517DA6" >
<textPath id="result"
xlink:href="#train">
<tspan dx="0" > 🚂 </tspan>
<tspan dx="-12"> - </tspan>
<tspan dx="-15"> 🚃</tspan>
<tspan dx="-12"> -</tspan>
<tspan dx="-15"> 🚃 </tspan>
<tspan dx="-12"> - </tspan>
<tspan dx="-15"> 🚃</tspan>
<tspan dx="-12"> -</tspan>
<tspan dx="-15"> 🚃 </tspan>
<tspan dx="-12"> - </tspan>
<tspan dx="-15"> 🚃</tspan>
<tspan dx="-12"> -</tspan>
<tspan dx="-15" > 🚂 </tspan>
<animate id="anTrain"
begin="0s;an5.end"
dur="12s"
repeatCount="1"
attributeName="startOffset"
values="-60%;45%;45%;-60%"
fill="freeze"/>
</textPath>
</text>
<path id="walk"
stroke-dasharray="409"
stroke-dashoffset="409"
stroke-width="3"
d="m343 268 34-10 50-9-33-86 22-14 7-21 8-3 13 18 34-25 47 65 22-19"
style="fill:none;stroke:#B34EE9">
<animate id="anPathWalk"
attributeName="stroke-dashoffset"
begin="anTrain.end-7.5s"
dur="2s"
values="409;0"
fill="freeze" />
</path>
<use xlink:href="#Man" transform="translate(0,0) scale(1.2)" style="stroke:blue; fill:black;">
<animateMotion id="an2"
begin="anPathWalk.end"
dur="16s"
repeatCount="1" >
<mpath xlink:href="#walk"/>
</animateMotion>
</use>
<use xlink:href="#Man" transform="translate(0,0) scale(1.2)" style="stroke:crimson;" >
<animateMotion id="an3"
begin="anPathWalk.end+0.5s"
dur="17s" repeatCount="1" >
<mpath xlink:href="#walk"/>
</animateMotion>
</use>
<use xlink:href="#Man"
transform="translate(0,0)
scale(1)"
style="stroke:black;">
<animateMotion id="an4"
begin="anPathWalk.end+1s"
dur="13s"
repeatCount="1" >
<mpath xlink:href="#walk"/>
</animateMotion>
</use>
<use xlink:href="#Man"
transform="translate(0,0)
scale(0.8)"
style="stroke:red;
fill:black;">
<animateMotion id="an5"
begin="anPathWalk.end+1.5s"
dur="11s"
repeatCount="1" >
<mpath xlink:href="#walk"/>
</animateMotion>
</use>
<use xlink:href="#Man"
transform="translate(0,0)
scale(0.8)"
style="stroke:black;">
<animateMotion id="an5"
begin="anPathWalk.end+1.8s"
dur="9.5s" repeatCount="1" >
<mpath xlink:href="#walk"/>
</animateMotion>
</use>
</svg>
<audio src="https://svg-art.ru/files/Time_Machine.mp3" autoplay="autoplay"></audio>
</div>
Возможно ли максимально автоматизировать прокладку маршрута из одной точки карты в другую с помощью создания ломанных отрезков path
щёлкая мышкой по контрольным точкам карты, - у разветвления улиц, при изменении направления движения.
Другими словами, - работа программы должна повторять работу в векторном редакторе, когда мы наносим узловые точки, которые автоматически соединяются отрезками линий, а на выходе мы получаем готовую формулу патча.
Есть топик, в котором прокладка маршрутов реализована, но она требует указания координат вершин ломанных линий вручную.
Первый раз работаю с Яндекс картами, был неприятно удивлен, что они поддерживают только целочисленное значение zoom, это осложняет синхронизацию карты с svg картинкой, в которой zoom может быть дробный.
Решение сделано при помощи d3.js, сперва необходимо при помощи мыши спозиционировать карту, затем нажать кнопку и появится svg оверлей, по которому можно кликать, прокладывая маршрут. В этом режиме drag правой кнопкой мыши таскает карту, клики левой добавляют точки в маршрут, даблклики по точкам удаляют их. Для того, чтобы получить SVG результат в виде SVG кода, нажмите на кнопку
Вот как это выглядит:
PS: принимаю предложения по анимации маршрута и другие предложения.
<script src="https://api-maps.yandex.ru/2.1/?lang=ru_RU"></script>
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.7.2/css/all.min.css" rel="stylesheet"/>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
<style>
body, #map, #overlay, svg, #bg {
position: absolute;
margin: 0;
width: 100vw;
height: 100vh;
overflow: hidden;
}
#bg {
top: 0;
left: 0;
background-color: rgba(0,0,0,0.5);
pointer-events: all;
}
button, textarea {
pointer-events: all;
}
textarea {
position: absolute;
width: 400px;
height: 300px;
left: 50%;
top: 50%;
transform: translate(-50%,-50%)
}
#ui {
pointer-events: none;
position: absolute;
padding: 5px;
}
path {
fill: none;
stroke-width: 2.2;
stroke: red;
}
circle {
pointer-events: all;
stroke: red;
stroke-width: 1.5px;
fill: #fff;
fill-opacity: .2;
cursor: move;
}
.selected {
fill: #ff7f0e;
stroke: #ff7f0e;
}
.hidden {
display: none !important;
}
.buttons {
position: absolute;
width: 150px;
}
button {
display: inline;
}
</style>
<div id="map"></div>
<div id="overlay" class='hidden'>
<svg></svg>
</div>
<div id="ui">
<div id="bg" class='hidden'>
<textarea></textarea>
</div>
<div class="buttons">
<button class='fa fa-2x fa-code'></button>
<button class='fa fa-2x fa-route'></button>
</div>
</div>
<script>
let lat = 60;
let lon = 30.3;
let zoom = 15;
let yMaps;
let points = [];
let transform = {};
let dragged = null;
let selected = points[points.length-1];
let line = d3.line().curve(d3.curveLinear);
let svg = d3.select("svg");
let canvas = svg.append('g');
let path = canvas.append("path")
.datum(points);
svg.on("mousedown", mousedown)
.on("mousemove", mousemove)
d3.select(window)
.on("mouseup", mouseup)
.on("resize", adjustSize);
d3.select('.fa-route').on('click', showOverlay);
d3.select('.fa-code').on('click', showCode);
window.oncontextmenu = () => false;
ymaps.ready(startmap);
svg.call(createZoom());
adjustSize();
redraw();
function showCode() {
toggleElement('#bg');
let div = d3.select(document.createElement('div'))
.html(svg.node().outerHTML)
div.select('svg')
.attr('zoom', yMaps.getZoom())
.attr('lat', yMaps.getCenter()[0])
.attr('lon', yMaps.getCenter()[1]);
div.selectAll('circle').remove();
d3.select('#bg textarea').html(div.node().innerHTML);
}
function showOverlay() {
lat = yMaps.getCenter()[0];
lon = yMaps.getCenter()[1];
zoom = yMaps.getZoom();
d3.select(this).classed('hidden', true);
toggleElement('#overlay', false);
}
function toggleElement(selector, isVisible) {
return d3.select(selector)
.node()
.classList
.toggle('hidden', isVisible)
}
function applyTransform() {
transform = d3.event.transform;
canvas.attr("transform", transform);
onTransform(transform);
}
function createZoom() {
return d3.zoom()
.filter(() => d3.event.button === 2)
.scaleExtent([1, 1])
.on("zoom", applyTransform);
}
function adjustSize() {
let w = window.innerWidth;
let h = window.innerHeight;
svg.attr("width", w).attr("height", h)
.attr("viewBox", `${-w/2} ${-h/2} ${w} ${h}`);
}
function redraw() {
canvas.select("path").attr("d", line);
var circle = canvas.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();
})
.on("dblclick", deletePoint)
.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 mousedown() {
if (d3.event.button !== 0)
return;
points.push(selected = dragged =
d3.mouse(canvas.node()));
redraw();
}
function mousemove() {
if (!dragged)
return;
let m = d3.mouse(canvas.node());
dragged[0] = m[0];
dragged[1] = m[1];
redraw();
}
function mouseup() {
if (!dragged)
return;
mousemove();
dragged = null;
}
function deletePoint(d) {
if (!selected)
return;
let i = points.indexOf(selected);
points.splice(i, 1);
selected = points.length ?
points[i > 0 ? i - 1 : 0] : null;
redraw();
}
function startmap() {
yMaps = new ymaps.Map("map", {
center: [lat, lon],
zoom: 15,
controls: []
});
}
function onTransform(transform) {
var merc = Math.cos(yMaps.getCenter()[0]*Math.PI/180);
var z = zoom;// + Math.log2(transform.k);
var s = Math.pow(2, z-1) * 256 / 180 / merc;
yMaps.setCenter([
lat + transform.y / s,
lon - transform.x / s / merc,
]);
// yMaps.setZoom(z+1);
}
</script>
P.S. Карты могут быть и не яндекс
Информации, скриншотов, сниппетов достаточно много, поэтому решил оформить отдельным ответом, так как этот объем не поместится в комментариях.
Хочу остановиться на некоторых аспектах применения замечательного решения @Stranger in the Q
Как я попытался применить данное решение:
Копирую код в отдельный файл, в котором добавлен скриншот карты с помощью команды <image>
<div class="container">
<svg version="1.1" xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
width="1400" height="858" viewBox="-700 -429 1400 858" zoom="14" lat="60.02023833197165" lon="30.424723377333542">
<image xlink:href="https://i.stack.imgur.com/xKUKV.png" width="100%" height="100%" />
<path stroke="red" fill="none" d="M-669,34L-434,-160L-218,-47L-202,-50L-199,-40L-194,-28L-147,-7L1,67L99,116L96,75L93,-5L88,-89L86,-158L86,-297L-47,-352L-161,-130L-63,-71L-9,-173"></path>
</svg>
</div>
viewBox="-700 -429 1400 858"
viewBox="0 0 1400 858"
<div class="container">
<svg version="1.1" xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
width="1400" height="858" viewBox="0 0 1400 858" zoom="14" lat="60.02023833197165" lon="30.424723377333542">
<image xlink:href="https://i.stack.imgur.com/xKUKV.png" width="100%" height="100%" />
<path stroke="red" fill="none" d="M-669,34L-434,-160L-218,-47L-202,-50L-199,-40L-194,-28L-147,-7L1,67L99,116L96,75L93,-5L88,-89L86,-158L86,-297L-47,-352L-161,-130L-63,-71L-9,-173"></path>
</svg>
</div>
Path
маршрута Чтобы вернуть Path
на место добавляю команду к <path transform="translate(700 429)"
<div class="container">
<svg version="1.1" xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
width="1400" height="858" viewBox="0 0 1400 858" zoom="14" lat="60.02023833197165" lon="30.424723377333542">
<image xlink:href="https://i.stack.imgur.com/xKUKV.png" width="100%" height="100%" />
<path transform="translate(700 429)" d="M-669,34L-434,-160L-218,-47L-202,-50L-199,-40L-194,-28L-147,-7L1,67L99,116L96,75L93,-5L88,-89L86,-158L86,-297L-47,-352L-161,-130L-63,-71L-9,-173" stroke="red" stroke-width="2" fill="none"></path>
</svg>
</div>
Выводы:
чтобы получить исходное положение маршрута надо в коде, который выводит программа сделать следующие изменения
Заменить отрицательные значения viewBox="-700 -429 1400 858"
на нулевые viewBox="0 0 1400 858"
Добавить команду к <path transform="translate(700 429)"
по маршруту, полученному из программы @Stranger in the Q
<div class="container">
<svg version="1.1" xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
width="1400" height="858" viewBox="0 0 1400 858" zoom="14" lat="60.02023833197165" lon="30.424723377333542">
<defs>
<path transform="translate(700 429)" d="M-669,34L-434,-160L-218,-47L-202,-50L-199,-40L-194,-28L-147,-7L1,67L99,116L96,75L93,-5L88,-89L86,-158L86,-297L-47,-352L-161,-130L-63,-71L-9,-173" stroke="red" stroke-width="2" fill="none">
</path>
<g id="Man" transform="translate(700 429) scale(1.5,-1.5)">
<path fill="none">
<animate
attributeName="d"
begin="0s"
dur="0.25s"
repeatCount="indefinite"
values="M-3,0 0,10 3,0 M0,10 0,16 l 4,-5 M0,16 l-4,-5 M0,16 c4,4 -4,4 0,0;
M 0,0 0,10 0,0 M0,10 0,16 l 0,-5 M0,16 l 0,-5 M0,16 c4,4 -4,4 0,0;
M-3,0 0,10 3,0 M0,10 0,16 l 4,-5 M0,16 l-4,-5 M0,16 c4,4 -4,4 0,0"/>
</path>
</g>
</defs>
<image xlink:href="https://i.stack.imgur.com/xKUKV.png" width="100%" height="100%" />
<path id="walk" transform="translate(700 429)" d="M-669,34L-434,-160L-218,-47L-202,-50L-199,-40L-194,-28L-147,-7L1,67L99,116L96,75L93,-5L88,-89L86,-158L86,-297L-47,-352L-161,-130L-63,-71L-9,-173" stroke="red" stroke-width="2" fill="none">
</path>
<use xlink:href="#Man" style="stroke:blue; fill:none;">
<animateMotion id="an1"
begin="0s"
dur="20s"
repeatCount="indefinite" >
<mpath xlink:href="#walk"/>
</animateMotion>
</use>
<use xlink:href="#Man" style="stroke:purple; fill:none;">
<animateMotion id="an2"
begin="an1.begin+2s"
dur="19s"
repeatCount="indefinite" >
<mpath xlink:href="#walk"/>
</animateMotion>
</use>
<use xlink:href="#Man" style="stroke:green; fill:none;">
<animateMotion id="an3"
begin="an2.begin+1s"
dur="18s"
repeatCount="indefinite" >
<mpath xlink:href="#walk"/>
</animateMotion>
</use>
</svg>
</div>
Оборудование для ресторана: новинки профессиональной кухонной техники
Частный дом престарелых в Киеве: комфорт, забота и профессиональный уход
Я только начал изучать JS, до этого (как и сейчас) кодю на C#Для меня неявная типизация JS - просто ужас! Появился конкретный кейс, код успешно...
У меня есть функция:
Я не очень сильна в JS, помогите пожалуйста решить вопросЕсть div, который нужно отображать каждые условно 5 минут
Есть несколько товаров с кнопкой заказать, при нажатии ничего не происходит, и ещё вопрос как можно передать название именно того товара...