Subway
You can create maps that can zoom using three axes.
- JavaScript
- React
- Vue@2
- Vue@3
- Svelte
<div>
<p>You can create maps that can zoom using three axes.</p>
<div id="zoomWrapper">
<img id="subway" src="../image/subway/subway.png"/>
</div>
</div>
function getZoomedOffset(value, zoom, beforeZoom) {
return -(value/zoom - value/beforeZoom);
}
const SUPPORT_TOUCH = "ontouchstart" in window;
const IMAGE_SIZE = 3000;
const wrapper = document.getElementById("zoomWrapper");
const wrapperSize = wrapper.getBoundingClientRect().width;
wrapper.style.height = wrapperSize + "px";
const imageView = document.getElementById("subway");
const baseScale = wrapperSize / IMAGE_SIZE;
// 1. Initialize eg.Axes
const axes = new eg.Axes({
x: {
range: [0, 0],
bounce: 100
},
y: {
range: [0, 0],
bounce: 100
},
zoom: {
range: [baseScale, 1]
}
}, {
deceleration: 0.003,
interrutable: false
}, {
zoom: baseScale
});
// 2. attach event handler
axes.on("change", ({pos, delta, inputEvent, set}) => {
if(inputEvent && delta.zoom) {
const center = SUPPORT_TOUCH ? inputEvent.center : {
x: inputEvent.layerX,
y: inputEvent.layerY
};
const beforeZoom = pos.zoom - delta.zoom;
const newX = pos.x + getZoomedOffset(center.x, pos.zoom, beforeZoom);
const newY = pos.y + getZoomedOffset(center.y, pos.zoom, beforeZoom);
set({x: newX, y: newY});
imageView.style[eg.Axes.TRANSFORM] =
`scale(${pos.zoom}) translate3d(${-newX}px, ${-newY}px, 0)`;
// change view
axes.axis.y.range[1] = axes.axis.x.range[1] =
axes.axis.x.range[1] + getZoomedOffset(wrapperSize, pos.zoom, beforeZoom);
} else {
imageView.style[eg.Axes.TRANSFORM] =
`scale(${pos.zoom}) translate3d(${-pos.x}px, ${-pos.y}px, 0)`;
}
});
// 3. Initialize inputTypes and connect it
axes.connect("zoom", SUPPORT_TOUCH ?
new eg.Axes.PinchInput(wrapper) :
new eg.Axes.WheelInput(wrapper, {
scale: Math.abs(baseScale)
})
).connect("x y", new eg.Axes.PanInput(wrapper, {
scale: [-1, -1]
}));
import React, { useState, useEffect, useRef } from "react";
import { useAxes, PanInput, PinchInput, WheelInput } from "@egjs/react-axes";
import "../../css/demos/subway.css";
export default function Subway() {
const [currentPos, setCurrentPos] = useState({ x: 0, y: 0 });
const [range, setRange] = useState(0);
const wrapper = useRef<HTMLDivElement>(null);
const { connect, setTo, setAxis, onChange, zoom } = useAxes(
{
x: {
range: [0, 0],
bounce: 100,
},
y: {
range: [0, 0],
bounce: 100,
},
zoom: {
range: [0, 1],
},
},
{
deceleration: 0.003,
},
);
const getZoomedOffset = (value, zoom, beforeZoom) => {
return -(value / zoom - value / beforeZoom);
};
onChange(({ pos, delta, inputEvent, set }) => {
const SUPPORT_TOUCH = "ontouchstart" in window;
if (inputEvent && delta.zoom) {
const center = SUPPORT_TOUCH
? inputEvent.center
: {
x: inputEvent.layerX,
y: inputEvent.layerY,
};
const beforeZoom = pos.zoom - delta.zoom;
const newX = pos.x + getZoomedOffset(center.x, pos.zoom, beforeZoom);
const newY = pos.y + getZoomedOffset(center.y, pos.zoom, beforeZoom);
const wrapperSize = wrapper.current.getBoundingClientRect().width;
const newRange = range + getZoomedOffset(wrapperSize, pos.zoom, beforeZoom);
setCurrentPos({ x: newX, y: newY });
set({ x: newX, y: newY });
setRange(newRange);
setAxis({
x: {
range: [0, newRange],
},
y: {
range: [0, newRange],
},
});
} else {
setCurrentPos({ x: pos.x, y: pos.y });
}
});
useEffect(() => {
const SUPPORT_TOUCH = "ontouchstart" in window;
const IMAGE_SIZE = 3000;
const wrapperSize = wrapper.current.getBoundingClientRect().width;
const baseScale = wrapperSize / IMAGE_SIZE;
wrapper.current.style.height = wrapperSize + "px";
setAxis({
zoom: {
range: [baseScale, 1],
},
});
setTo({
zoom: baseScale,
});
connect("zoom", SUPPORT_TOUCH ? new PinchInput(wrapper) : new WheelInput(wrapper, { scale: Math.abs(baseScale) }));
connect("x y", new PanInput(wrapper, { scale: [-1, -1] }));
}, []);
return (
<div>
<p>You can create maps that can zoom using three axes.</p>
<div id="zoomWrapper" ref={wrapper}>
<img
id="subway"
src={require("@site/static/img/demos/subway/subway.png").default}
style={{ transform: `scale(${zoom}) translate3d(${-currentPos.x}px, ${-currentPos.y}px, 0)` }}
/>
</div>
</div>
);
};
<template>
<div class="App">
<p>You can create maps that can zoom using three axes.</p>
<div id="zoomWrapper" ref="wrapper">
<img
id="subway"
:src="`${require('../../../static/img/demos/subway/subway.png')}`"
:style="{ transform: `scale(${zoom}) translate3d(${-currentPos.x}px, ${-currentPos.y}px, 0)` }"
/>
</div>
</div>
</template>
<script lang="ts">
import { ref, defineComponent, onMounted } from "@vue/composition-api";
import { useAxes, PanInput, PinchInput, WheelInput } from "@egjs/vue2-axes";
export default defineComponent({
name: "App",
setup() {
const currentPos = ref({ x: 0, y: 0 });
const range = ref(0);
const wrapper = ref(null);
const { connect, setTo, setAxis, onChange, zoom } = useAxes(
{
x: {
range: [0, 0],
bounce: 100,
},
y: {
range: [0, 0],
bounce: 100,
},
zoom: {
range: [0, 1],
},
},
{
deceleration: 0.003,
},
);
const getZoomedOffset = (value, zoom, beforeZoom) => {
return -(value / zoom - value / beforeZoom);
};
onChange(({ pos, delta, inputEvent, set }) => {
const SUPPORT_TOUCH = "ontouchstart" in window;
if (inputEvent && delta.zoom) {
const center = SUPPORT_TOUCH
? inputEvent.center
: {
x: inputEvent.layerX,
y: inputEvent.layerY,
};
const beforeZoom = pos.zoom - delta.zoom;
const newX = pos.x + getZoomedOffset(center.x, pos.zoom, beforeZoom);
const newY = pos.y + getZoomedOffset(center.y, pos.zoom, beforeZoom);
const wrapperSize = wrapper.value.getBoundingClientRect().width;
const newRange = range.value + getZoomedOffset(wrapperSize, pos.zoom, beforeZoom);
currentPos.value = { x: newX, y: newY };
set({ x: newX, y: newY });
range.value = newRange;
setAxis({
x: {
range: [0, newRange],
},
y: {
range: [0, newRange],
},
});
} else {
currentPos.value = { x: pos.x, y: pos.y };
}
});
onMounted(() => {
const SUPPORT_TOUCH = "ontouchstart" in window;
const IMAGE_SIZE = 3000;
const wrapperSize = wrapper.value.getBoundingClientRect().width;
const baseScale = wrapperSize / IMAGE_SIZE;
wrapper.value.style.height = wrapperSize + "px";
setAxis({
zoom: {
range: [baseScale, 1],
},
});
setTo({
zoom: baseScale,
});
connect("zoom", SUPPORT_TOUCH ? new PinchInput(wrapper) : new WheelInput(wrapper, { scale: Math.abs(baseScale) }));
connect("x y", new PanInput(wrapper, { scale: [-1, -1] }));
});
return {
wrapper,
currentPos,
zoom,
};
},
});
</script>
<template>
<div class="App">
<p>You can create maps that can zoom using three axes.</p>
<div id="zoomWrapper" ref="wrapper">
<img
id="subway"
:src="`${require('../../../static/img/demos/subway/subway.png')}`"
:style="{ transform: `scale(${zoom}) translate3d(${-currentPos.x}px, ${-currentPos.y}px, 0)` }"
/>
</div>
</div>
</template>
<script lang="ts">
import { ref, defineComponent, onMounted } from "vue";
import { useAxes, PanInput, PinchInput, WheelInput } from "@egjs/vue-axes";
export default defineComponent({
name: "App",
setup() {
const currentPos = ref({ x: 0, y: 0 });
const range = ref(0);
const wrapper = ref(null);
const { connect, setTo, setAxis, onChange, zoom } = useAxes(
{
x: {
range: [0, 0],
bounce: 100,
},
y: {
range: [0, 0],
bounce: 100,
},
zoom: {
range: [0, 1],
},
},
{
deceleration: 0.003,
},
);
const getZoomedOffset = (value, zoom, beforeZoom) => {
return -(value / zoom - value / beforeZoom);
};
onChange(({ pos, delta, inputEvent, set }) => {
const SUPPORT_TOUCH = "ontouchstart" in window;
if (inputEvent && delta.zoom) {
const center = SUPPORT_TOUCH
? inputEvent.center
: {
x: inputEvent.layerX,
y: inputEvent.layerY,
};
const beforeZoom = pos.zoom - delta.zoom;
const newX = pos.x + getZoomedOffset(center.x, pos.zoom, beforeZoom);
const newY = pos.y + getZoomedOffset(center.y, pos.zoom, beforeZoom);
const wrapperSize = wrapper.value.getBoundingClientRect().width;
const newRange = range.value + getZoomedOffset(wrapperSize, pos.zoom, beforeZoom);
currentPos.value = { x: newX, y: newY };
set({ x: newX, y: newY });
range.value = newRange;
setAxis({
x: {
range: [0, newRange],
},
y: {
range: [0, newRange],
},
});
} else {
currentPos.value = { x: pos.x, y: pos.y };
}
});
onMounted(() => {
const SUPPORT_TOUCH = "ontouchstart" in window;
const IMAGE_SIZE = 3000;
const wrapperSize = wrapper.value.getBoundingClientRect().width;
const baseScale = wrapperSize / IMAGE_SIZE;
wrapper.value.style.height = wrapperSize + "px";
setAxis({
zoom: {
range: [baseScale, 1],
},
});
setTo({
zoom: baseScale,
});
connect("zoom", SUPPORT_TOUCH ? new PinchInput(wrapper) : new WheelInput(wrapper, { scale: Math.abs(baseScale) }));
connect("x y", new PanInput(wrapper, { scale: [-1, -1] }));
});
return {
wrapper,
currentPos,
zoom,
};
},
});
</script>
<script>
import { onMount } from "svelte";
import { useAxes, PanInput, PinchInput, WheelInput } from "@egjs/svelte-axes";
const SUPPORT_TOUCH = "ontouchstart" in window;
let currentPos = { x: 0, y: 0 };
let range = 0;
let wrapper;
const { connect, setTo, setAxis, onChange, zoom } = useAxes(
{
x: {
range: [0, 0],
bounce: 100,
},
y: {
range: [0, 0],
bounce: 100,
},
zoom: {
range: [0, 1],
},
},
{
deceleration: 0.003,
},
);
const getZoomedOffset = (value, zoom, beforeZoom) => {
return -(value / zoom - value / beforeZoom);
};
onChange(({ pos, delta, inputEvent, set }) => {
if (inputEvent && delta.zoom) {
const center = SUPPORT_TOUCH
? inputEvent.center
: {
x: inputEvent.layerX,
y: inputEvent.layerY,
};
const beforeZoom = pos.zoom - delta.zoom;
const newX = pos.x + getZoomedOffset(center.x, pos.zoom, beforeZoom);
const newY = pos.y + getZoomedOffset(center.y, pos.zoom, beforeZoom);
const wrapperSize = wrapper.getBoundingClientRect().width;
const newRange = range + getZoomedOffset(wrapperSize, pos.zoom, beforeZoom);
currentPos = { x: newX, y: newY };
set({ x: newX, y: newY });
range = newRange;
setAxis({
x: {
range: [0, newRange],
},
y: {
range: [0, newRange],
},
});
} else {
currentPos = { x: pos.x, y: pos.y };
}
});
onMount(() => {
const IMAGE_SIZE = 3000;
const wrapperSize = wrapper.getBoundingClientRect().width;
const baseScale = wrapperSize / IMAGE_SIZE;
wrapper.style.height = wrapperSize + "px";
setAxis({
zoom: {
range: [baseScale, 1],
},
});
setTo({
zoom: baseScale,
});
connect("zoom", SUPPORT_TOUCH ? new PinchInput(wrapper) : new WheelInput(wrapper, { scale: Math.abs(baseScale) }));
connect("x y", new PanInput(wrapper, { scale: [-1, -1] }));
});
</script>
<div>
<p>You can create maps that can zoom using three axes.</p>
<div id="zoomWrapper" bind:this={wrapper}>
<img
id="subway"
src="../image/subway/subway.png"
style="transform: scale({$zoom}) translate3d({-currentPos.x}px, {-currentPos.y}px, 0)"
/>
</div>
</div>