π ΠΠ°ΡΡΡΠ°Π±ΠΈΡΠΎΠ²Π°Π½ΠΈΠ΅ ΠΈ ΠΏΠ°Π½ΠΎΡΠ°ΠΌΠΈΡΠΎΠ²Π°Π½ΠΈΠ΅ Π² 69 ΡΡΡΠΎΡΠΊΠ°Ρ JavaScript
Π§ΡΠΎΠ±Ρ Π½Π΅ ΠΈΡΠΏΠΎΠ»ΡΠ·ΠΎΠ²Π°ΡΡ ΡΡΠΆΠ΅Π»ΠΎΠ²Π΅ΡΠ½ΡΠ΅ Π±ΠΈΠ±Π»ΠΈΠΎΡΠ΅ΠΊΠΈ, ΠΌΠΎΠΆΠ½ΠΎ Π½Π°ΠΏΠΈΡΠ°ΡΡ Π½Π° ΡΠΈΡΡΠΎΠΌ JavaScript ΠΏΡΠΎΡΡΠΎΠ΅ ΠΈ ΡΠ°ΡΡΠΈΡΡΠ΅ΠΌΠΎΠ΅ ΡΠ΅ΡΠ΅Π½ΠΈΠ΅ Π΄Π»Ρ ΠΌΠ°Π½ΠΈΠΏΡΠ»ΡΡΠΈΠΉ Ρ ΡΠ»Π΅ΠΌΠ΅Π½ΡΠ°ΠΌΠΈ Π²Π΅Π±-ΡΡΡΠ°Π½ΠΈΡΡ.
ΠΠΎΠ·ΠΌΠΎΠΆΠ½ΠΎΡΡΡ ΠΌΠ°ΡΡΡΠ°Π±ΠΈΡΠΎΠ²Π°ΡΡ ΡΠ»Π΅ΠΌΠ΅Π½ΡΡ ΡΡΡΠ°Π½ΠΈΡΡ ΠΈ Π΄Π΅ΡΠ°Π»ΡΠ½ΠΎ ΡΠ°ΡΡΠΌΠ°ΡΡΠΈΠ²Π°ΡΡ ΠΈΡ β ΡΡΠΎ ΠΎΡΠ΅Π½Ρ ΠΊΡΡΡΠΎΠΉ ΠΏΠΎΠ»ΡΠ·ΠΎΠ²Π°ΡΠ΅Π»ΡΡΠΊΠΈΠΉ ΠΎΠΏΡΡ. Π‘ΡΡΠ΅ΡΡΠ²ΡΠ΅Ρ ΠΌΠ½ΠΎΠΆΠ΅ΡΡΠ²ΠΎ Π³ΠΎΡΠΎΠ²ΡΡ Π±ΠΈΠ±Π»ΠΈΠΎΡΠ΅ΠΊ Ρ ΠΏΠΎΠ΄ΠΎΠ±Π½ΠΎΠΉ ΡΡΠ½ΠΊΡΠΈΠΎΠ½Π°Π»ΡΠ½ΠΎΡΡΡΡ, Π½ΠΎ ΡΠ΅Π³ΠΎΠ΄Π½Ρ ΠΌΡ Π½Π°ΠΏΠΈΡΠ΅ΠΌ ΡΠΎΠ±ΡΡΠ²Π΅Π½Π½ΡΠΉ Π²Π΅Π»ΠΎΡΠΈΠΏΠ΅Π΄ Π½Π° ΡΠΈΡΡΠΎΠΌ JavaScript! ΠΠ°ΡΠ΅ΠΌ?
- Π‘ΡΠΎΡΠΎΠ½Π½ΠΈΠ΅ ΡΠ΅ΡΠ΅Π½ΠΈΡ ΡΠ°ΡΡΠΎ ΠΏΡΠ΅Π΄Π»Π°Π³Π°ΡΡ ΠΈΠ·Π±ΡΡΠΎΡΠ½ΡΡ ΡΡΠ½ΠΊΡΠΈΠΎΠ½Π°Π»ΡΠ½ΠΎΡΡΡ, ΠΊΠΎΡΠΎΡΠ°Ρ Π²Π°ΠΌ Π½Π΅ Π½ΡΠΆΠ½Π°, Π½ΠΎ Π±Π°Π½Π΄Π» ΠΏΡΠΈΠ»ΠΎΠΆΠ΅Π½ΠΈΡ ΡΠ²Π΅Π»ΠΈΡΠΈΠ²Π°Π΅Ρ.
- Π ΡΠΎΠΌΡ ΠΆΠ΅ ΡΡΠΎ Π·Π°ΠΌΠ΅ΡΠ°ΡΠ΅Π»ΡΠ½ΡΠΉ ΡΠ΅Π»Π»Π΅Π½Π΄ΠΆ, ΠΊΠΎΡΠΎΡΡΠΉ ΡΠ°ΡΡΠ΅Π²Π΅Π»ΠΈΡ Π²Π°Ρ ΠΌΠΎΠ·Π³ ΠΈ ΠΏΡΠΎΠΊΠ°ΡΠ°Π΅Ρ Π½Π°Π²ΡΠΊΠΈ ΠΏΡΠΎΠ³ΡΠ°ΠΌΠΌΠΈΡΠΎΠ²Π°Π½ΠΈΡ.
Π ΡΠ΅Π·ΡΠ»ΡΡΠ°ΡΠ΅ ΠΌΡ ΠΏΠΎΠ»ΡΡΠΈΠΌ ΠΎΡΠ΅Π½Ρ ΠΌΠ°Π»Π΅Π½ΡΠΊΡΡ (Π²ΡΠ΅Π³ΠΎ 69 ΡΡΡΠΎΡΠ΅ΠΊ ΠΊΠΎΠ΄Π°!), ΠΏΡΠΎΡΡΡΡ ΠΈ ΡΠ΄ΠΎΠ±Π½ΡΡ Π±ΠΈΠ±Π»ΠΈΠΎΡΠ΅ΡΠΊΡ Π΄Π»Ρ ΠΌΠ°ΡΡΡΠ°Π±ΠΈΡΠΎΠ²Π°Π½ΠΈΡ ΠΈ ΠΏΠ°Π½ΠΎΡΠ°ΠΌΠΈΡΠΎΠ²Π°Π½ΠΈΡ.
Π Π°Π·ΠΌΠ΅ΡΠΊΠ° ΠΈ ΡΡΠΈΠ»ΠΈ
Π‘ΠΎΠ·Π΄Π°Π΄ΠΈΠΌ HTML-ΡΡΡΠ°Π½ΠΈΡΡ ΠΈ ΡΠ°Π·ΠΌΠ΅ΡΡΠΈΠΌ Π½Π° Π½Π΅ΠΉ ΡΠ»Π΅ΠΌΠ΅Π½Ρ-ΠΊΠΎΠ½ΡΠ΅ΠΉΠ½Π΅Ρ (#container
). ΠΠ½ΡΡΡΡ ΠΏΠΎΠΌΠ΅ΡΡΠΈΠΌ ΡΠ°Π±ΠΎΡΡΡ ΠΎΠ±Π»Π°ΡΡΡ (.area
), ΠΊΠΎΡΠΎΡΡΡ ΠΌΡ ΠΈ Π±ΡΠ΄Π΅ΠΌ Π½Π΅ΠΏΠΎΡΡΠ΅Π΄ΡΡΠ²Π΅Π½Π½ΠΎ ΠΌΠ°ΡΡΡΠ°Π±ΠΈΡΠΎΠ²Π°ΡΡ ΠΈ ΠΏΠ°Π½ΠΎΡΠ°ΠΌΠΈΡΠΎΠ²Π°ΡΡ.
<div id="container"> <div class="area"> <div class="rectangle"></div> <div class="circle"></div> <div class="text-area"> <h1>Example line of text</p> </div> </div> </div>
ΠΠ½ΡΡΡΠΈ ΡΠ°Π±ΠΎΡΠ΅ΠΉ ΠΎΠ±Π»Π°ΡΡΠΈ Π½Π°Ρ ΠΎΠ΄ΡΡΡΡ Π½Π΅ΡΠΊΠΎΠ»ΡΠΊΠΎ ΡΠ»Π΅ΠΌΠ΅Π½ΡΠΎΠ², ΠΊΠΎΡΠΎΡΡΠ΅ Π½Π΅ Π½Π΅ΡΡΡ Π½ΠΈΠΊΠ°ΠΊΠΎΠΉ ΡΠΌΡΡΠ»ΠΎΠ²ΠΎΠΉ Π½Π°Π³ΡΡΠ·ΠΊΠΈ, Π° ΠΏΡΠΎΡΡΠΎ ΠΏΡΠ΅Π΄Π½Π°Π·Π½Π°ΡΠ΅Π½Ρ Π΄Π»Ρ Π΄Π΅ΠΌΠΎΠ½ΡΡΡΠ°ΡΠΈΠΈ ΡΠ°Π±ΠΎΡΡ ΠΊΠΎΠ΄Π°.
ΠΠΎΠ±Π°Π²ΠΈΠΌ ΡΠ°ΠΊΠΆΠ΅ Π½Π΅ΠΌΠ½ΠΎΠ³ΠΎ ΡΡΠΈΠ»Π΅ΠΉ Π΄Π»Ρ ΠΎΡΠΎΡΠΌΠ»Π΅Π½ΠΈΡ ΡΡΡΠ°Π½ΠΈΡΡ:
body { overflow: hidden; } #container { height: 100%; width: 100%; position: absolute; } .area { border: 1px dashed black; height: 80%; width: 80%; position: absolute; } .circle { height: 200px; width: 200px; background-color: navajowhite; border-radius: 50%; display: inline-block; position: relative; } .rectangle { background-color: navajowhite; height: 150px; width: 250px; position: relative; } .text-area { float: right; position: relative; }
ΠΠ»Ρ body
ΡΡΡΠ°Π½Π°Π²Π»ΠΈΠ²Π°Π΅ΠΌ overflow: hidden
. ΠΡΠΎ Π½ΡΠΆΠ½ΠΎ, ΡΡΠΎΠ±Ρ ΠΈΠ·Π±Π΅ΠΆΠ°ΡΡ ΠΏΠ΅ΡΠ΅ΠΏΠΎΠ»Π½Π΅Π½ΠΈΡ ΡΡΡΠ°Π½ΠΈΡΡ ΠΈ ΠΏΠΎΡΠ²Π»Π΅Π½ΠΈΡ ΠΏΡΠΎΠΊΡΡΡΠΊΠΈ ΠΏΡΠΈ ΡΡΠ΅Π·ΠΌΠ΅ΡΠ½ΠΎΠΌ ΡΠ²Π΅Π»ΠΈΡΠ΅Π½ΠΈΠΈ ΡΠ»Π΅ΠΌΠ΅Π½ΡΠ°.
Π’Π°ΠΊΠΆΠ΅ Π΄ΠΎΠ±Π°Π²ΠΈΠΌ ΡΠ°ΠΌΠΊΡ Π΄Π»Ρ Π²ΠΈΠ·ΡΠ°Π»ΡΠ½ΠΎΠ³ΠΎ ΠΎΠ±ΠΎΠ·Π½Π°ΡΠ΅Π½ΠΈΡ ΡΠ°Π±ΠΎΡΠ΅ΠΉ ΠΎΠ±Π»Π°ΡΡΠΈ (.area
) ΠΈ Π½Π΅ΠΌΠ½ΠΎΠ³ΠΎ ΠΎΠ±Π»Π°Π³ΠΎΡΠΎΠ΄ΠΈΠΌ Π΄Π΅ΠΌΠΎ-ΠΊΠΎΠ½ΡΠ΅Π½Ρ (ΠΊΠ»Π°ΡΡΡ .circle
, .rectangle
ΠΈ .text-area
).
Π‘ΠΊΡΠΈΠΏΡ Π±ΠΈΠ±Π»ΠΈΠΎΡΠ΅ΠΊΠΈ
ΠΠΎΠ΄ ΡΠ°ΠΌΠΎΠΉ Π±ΠΈΠ±Π»ΠΈΠΎΡΠ΅ΠΊΠΈ Π±ΡΠ΄Π΅Ρ ΡΠ°ΡΠΏΠΎΠ»Π°Π³Π°ΡΡΡΡ Π² ΡΠ°ΠΉΠ»Π΅ renderer.js
. ΠΠΊΡΠΏΠΎΡΡΠΈΡΡΠ΅ΠΌ ΠΈΠ· ΠΌΠΎΠ΄ΡΠ»Ρ Π³Π»Π°Π²Π½ΡΡ ΡΡΠ½ΠΊΡΠΈΡ renderer
:
const renderer = ({ minScale, maxScale, element, scaleSensitivity = 10 }) => { const state = { element, minScale, maxScale, scaleSensitivity, transformation: { originX: 0, originY: 0, translateX: 0, translateY: 0, scale: 1 }, }; return Object.assign({}, makeZoom(state), makePan(state)); }; module.exports = { renderer };
ΠΠ½Π° ΠΏΡΠΈΠ½ΠΈΠΌΠ°Π΅Ρ Π±Π°Π·ΠΎΠ²ΡΠ΅ ΠΏΠ°ΡΠ°ΠΌΠ΅ΡΡΡ:
minScale
β ΠΌΠΈΠ½ΠΈΠΌΠ°Π»ΡΠ½ΡΠΉ ΠΌΠ°ΡΡΡΠ°Π±;maxScale
β ΠΌΠ°ΠΊΡΠΈΠΌΠ°Π»ΡΠ½ΡΠΉ ΠΌΠ°ΡΡΡΠ°Π±;element
β DOM-ΡΠ»Π΅ΠΌΠ΅Π½Ρ, Ρ ΠΊΠΎΡΠΎΡΡΠΌ Π±ΡΠ΄ΡΡ ΠΏΡΠΎΠΈΠ·Π²ΠΎΠ΄ΠΈΡΡΡΡ ΠΌΠ°Π½ΠΈΠΏΡΠ»ΡΡΠΈΠΈ;scaleSensitivity
β ΠΊΠΎΡΡΡΠΈΡΠΈΠ΅Π½Ρ ΡΡΠ²ΡΡΠ²ΠΈΡΠ΅Π»ΡΠ½ΠΎΡΡΡ ΠΌΠ°ΡΡΡΠ°Π±ΠΈΡΠΎΠ²Π°Π½ΠΈΡ, ΠΏΠΎ ΡΠΌΠΎΠ»ΡΠ°Π½ΠΈΡ 10.
Π Π·Π°ΠΌΡΠΊΠ°Π½ΠΈΠΈ ΡΡΠ½ΠΊΡΠΈΠΈ ΡΠΎΠ·Π΄Π°Π΅ΡΡΡ ΠΎΠ±ΡΠ΅ΠΊΡ ΡΠΎΡΡΠΎΡΠ½ΠΈΡ β state
, ΠΊΠΎΡΠΎΡΡΠΉ Ρ
ΡΠ°Π½ΠΈΡ Π½Π°ΡΡΡΠΎΠΉΠΊΠΈ ΠΈ ΡΠΎΠ²Π΅ΡΡΠ΅Π½Π½ΡΠ΅ Π½Π°Π΄ ΡΠ»Π΅ΠΌΠ΅Π½ΡΠΎΠΌ ΠΏΡΠ΅ΠΎΠ±ΡΠ°Π·ΠΎΠ²Π°Π½ΠΈΡ (ΠΏΠΎΠ»Π΅ transformation
).
ΠΠ· ΡΡΠ½ΠΊΡΠΈΠΈ Π²ΠΎΠ·Π²ΡΠ°ΡΠ°Π΅ΡΡΡ ΠΎΠ±ΡΠ΅ΠΊΡ Ρ Π½Π°Π±ΠΎΡΠΎΠΌ ΠΌΠ΅ΡΠΎΠ΄ΠΎΠ². ΠΡΠΈ ΡΡΠΎΠΌ Π²ΠΎΠ·ΠΌΠΎΠΆΠ½ΠΎΡΡΠΈ ΠΌΠ°ΡΡΡΠ°Π±ΠΈΡΠΎΠ²Π°Π½ΠΈΡ ΠΈ ΠΏΠ°Π½ΠΎΡΠ°ΠΌΠΈΡΠΎΠ²Π°Π½ΠΈΡ ΡΠ°Π·Π΄Π΅Π»Π΅Π½Ρ Π½Π° ΠΎΡΠ΄Π΅Π»ΡΠ½ΡΠ΅ ΡΡΠ½ΠΊΡΠΈΠΈ-ΠΊΠΎΠ½ΡΡΡΡΠΊΡΠΎΡΡ β makeZoom
ΠΈ makePan
, ΠΊΠΎΡΠΎΡΡΠ΅ ΠΌΡ ΡΠ°Π·Π±Π΅ΡΠ΅ΠΌ ΡΡΡΡ ΠΏΠΎΠ·ΠΆΠ΅. ΠΠΎΠ½ΡΡΡΡΠΊΡΠΎΡΡ ΠΏΠΎΠ»ΡΡΠ°ΡΡ ΠΎΠ±ΡΠΈΠΉ ΠΎΠ±ΡΠ΅ΠΊΡ ΡΠΎΡΡΠΎΡΠ½ΠΈΡ ΠΈ Π²ΠΎΠ·Π²ΡΠ°ΡΠ°ΡΡ ΠΎΡΠ΄Π΅Π»ΡΠ½ΡΠΉ Π½Π°Π±ΠΎΡ ΠΌΠ΅ΡΠΎΠ΄ΠΎΠ² Π΄Π»Ρ Π²Π·Π°ΠΈΠΌΠΎΠ΄Π΅ΠΉΡΡΠ²ΠΈΡ Ρ Π½ΠΈΠΌ.
Π’Π°ΠΊΠΎΠΉ ΠΏΠΎΠ΄Ρ ΠΎΠ΄ Π½Π°Π·ΡΠ²Π°Π΅ΡΡΡ ΠΊΠΎΠΌΠΏΠΎΠ·ΠΈΡΠΈΠ΅ΠΉ ΠΈ ΠΏΠΎΠ·Π²ΠΎΠ»ΡΠ΅Ρ ΠΏΡΠΎΡΠ΅ Π΄ΠΎΠ±Π°Π²Π»ΡΡΡ Π½ΠΎΠ²ΡΡ ΡΡΠ½ΠΊΡΠΈΠΎΠ½Π°Π»ΡΠ½ΠΎΡΡΡ ΠΈ Π»Π΅Π³ΡΠ΅ ΡΠ΅ΡΡΠΈΡΠΎΠ²Π°ΡΡ ΠΏΡΠΈΠ»ΠΎΠΆΠ΅Π½ΠΈΠ΅.
Π’ΡΠ°Π½ΡΡΠΎΡΠΌΠ°ΡΠΈΠΈ
ΠΡΠ΅ ΠΌΠ°Π½ΠΈΠΏΡΠ»ΡΡΠΈΠΈ Ρ ΡΠ»Π΅ΠΌΠ΅Π½ΡΠΎΠΌ Π±ΡΠ΄ΡΡ ΠΏΡΠΎΠΈΠ·Π²ΠΎΠ΄ΠΈΡΡΡΡ ΡΠ΅ΡΠ΅Π· ΠΈΠ·ΠΌΠ΅Π½Π΅Π½ΠΈΠ΅ ΡΠ²ΠΎΠΉΡΡΠ²Π° transform
. ΠΠ»Ρ ΡΡΠΎΠ³ΠΎ ΠΈΡΠΏΠΎΠ»ΡΠ·ΡΠ΅ΠΌ CSS-ΡΡΠ½ΠΊΡΠΈΡ matrix
, ΠΊΠΎΡΠΎΡΠΎΠΉ Π½ΡΠΆΠ½ΠΎ ΠΏΠ΅ΡΠ΅Π΄Π°ΡΡ ΠΏΡΠ°Π²ΠΈΠ»ΡΠ½ΡΠ΅ ΠΏΠ°ΡΠ°ΠΌΠ΅ΡΡΡ ΠΌΠ°ΡΡΡΠ°Π±Π° (scale
) ΠΈ ΡΠ΄Π²ΠΈΠ³Π° (translateX
ΠΈ translateY
):
const getMatrix = ({ scale, translateX, translateY }) => `matrix(${scale}, 0, 0, ${scale}, ${translateX}, ${translateY})`;
ΠΡΠΏΠΎΠΌΠΎΠ³Π°ΡΠ΅Π»ΡΠ½Π°Ρ ΡΡΠ½ΠΊΡΠΈΡ getMatrix
ΠΏΡΠΎΡΡΠΎ ΡΠΎΡΠΌΠΈΡΡΠ΅Ρ ΡΠ°Π±Π»ΠΎΠ½Π½ΡΡ ΡΡΡΠΎΠΊΡ ΠΏΡΠ°Π²ΠΈΠ»ΡΠ½ΠΎΠ³ΠΎ ΡΠΎΡΠΌΠ°ΡΠ°, ΠΊΠΎΡΠΎΡΡΡ Π½ΡΠΆΠ½ΠΎ ΡΡΡΠ°Π½ΠΎΠ²ΠΈΡΡ Π² ΡΠ²ΠΎΠΉΡΡΠ²ΠΎ style.transform
ΡΠ»Π΅ΠΌΠ΅Π½ΡΠ°.
ΠΠ°Π½ΠΎΡΠ°ΠΌΠΈΡΠΎΠ²Π°Π½ΠΈΠ΅
ΠΡΠΈ ΠΏΠ°Π½ΠΎΡΠ°ΠΌΠΈΡΠΎΠ²Π°Π½ΠΈΠΈ Π΄ΠΎΠ»ΠΆΠ½ΠΎ ΠΈΠ·ΠΌΠ΅Π½ΡΡΡΡΡ ΠΏΠΎΠ»ΠΎΠΆΠ΅Π½ΠΈΠ΅ ΡΠ»Π΅ΠΌΠ΅Π½ΡΠ° Π½Π° ΡΡΡΠ°Π½ΠΈΡΠ΅, ΡΠΎ Π΅ΡΡΡ ΠΏΡΠΎΠΈΠ·Π²ΠΎΠ΄ΠΈΡΡΡΡ Π΅Π³ΠΎ ΡΠ΄Π²ΠΈΠ³. Π€ΡΠ½ΠΊΡΠΈΡ pan
ΠΏΡΠΈΠ½ΠΈΠΌΠ°Π΅Ρ ΡΠ΅ΠΊΡΡΠ΅Π΅ ΡΠΎΡΡΠΎΡΠ½ΠΈΠ΅ ΡΠ»Π΅ΠΌΠ΅Π½ΡΠ° (state
), Π° ΡΠ°ΠΊΠΆΠ΅ Π½ΠΎΠ²ΡΠ΅ ΠΊΠΎΠΎΡΠ΄ΠΈΠ½Π°ΡΡ. ΠΠ°ΡΠ΅ΠΌ ΠΎΠ½Π° ΠΎΠ±Π½ΠΎΠ²Π»ΡΠ΅Ρ ΡΠΎΡΡΠΎΡΠ½ΠΈΠ΅, ΠΏΡΠΈΠ±Π°Π²Π»ΡΡ Π½ΠΎΠ²ΡΠΉ ΡΠ΄Π²ΠΈΠ³ ΠΊ ΡΠ΅ΠΊΡΡΠ΅ΠΌΡ ΠΏΠΎΠ»ΠΎΠΆΠ΅Π½ΠΈΡ ΠΈ ΠΎΠ±Π½ΠΎΠ²Π»ΡΠ΅Ρ ΡΠ²ΠΎΠΉΡΡΠ²ΠΎ style
ΡΠ»Π΅ΠΌΠ΅Π½ΡΠ°.
const pan = ({ state, originX, originY }) => { state.transformation.translateX += originX; state.transformation.translateY += originY; state.element.style.transform = getMatrix({ scale: state.transformation.scale, translateX: state.transformation.translateX, translateY: state.transformation.translateY }); };
Π’Π΅ΠΏΠ΅ΡΡ ΡΠ΅Π°Π»ΠΈΠ·ΡΠ΅ΠΌ Π΄Π²Π° ΠΌΠ΅ΡΠΎΠ΄Π°:
panBy
β ΠΏΡΠΎΡΡΠΎΠΉ ΡΠ΄Π²ΠΈΠ³ Π½Π° ΡΠΊΠ°Π·Π°Π½Π½ΡΠ΅ ΠΊΠΎΠΎΡΠ΄ΠΈΠ½Π°ΡΡ;panTo
β ΡΠ΄Π²ΠΈΠ³ Ρ ΠΎΠ΄Π½ΠΎΠ²ΡΠ΅ΠΌΠ΅Π½Π½ΡΠΌ ΠΌΠ°ΡΡΡΠ°Π±ΠΈΡΠΎΠ²Π°Π½ΠΈΠ΅ΠΌ.
const makePan = (state) => ({ panBy: ({ originX, originY }) => pan({ state, originX, originY }), panTo: ({ originX, originY, scale }) => { state.transformation.scale = scale; pan({ state, originX: originX - state.transformation.translateX, originY: originY - state.transformation.translateY }); }, });
ΠΡΠΈ ΡΠ΄Π²ΠΈΠ³Π΅ Ρ ΠΌΠ°ΡΡΡΠ°Π±ΠΈΡΠΎΠ²Π°Π½ΠΈΠ΅ΠΌ ΠΊΠΎΠΎΡΠ΄ΠΈΠ½Π°ΡΡ ΡΠ»Π΅ΠΌΠ΅Π½ΡΠ° Π½ΡΠΆΠ½ΠΎ ΡΠΊΠΎΡΡΠ΅ΠΊΡΠΈΡΠΎΠ²Π°ΡΡ.
ΠΠ°ΡΡΡΠ°Π±ΠΈΡΠΎΠ²Π°Π½ΠΈΠ΅
ΠΠ»Ρ ΠΈΠ·ΠΌΠ΅Π½Π΅Π½ΠΈΡ ΡΠ°Π·ΠΌΠ΅ΡΠ° ΡΠ»Π΅ΠΌΠ΅Π½ΡΠ° Π½Π°ΠΌ ΠΏΠΎΡΡΠ΅Π±ΡΠ΅ΡΡΡ Π½Π΅ΡΠΊΠΎΠ»ΡΠΊΠΎ Π²ΡΠΏΠΎΠΌΠΎΠ³Π°ΡΠ΅Π»ΡΠ½ΡΡ ΡΡΠ½ΠΊΡΠΈΠΉ Π΄Π»Ρ ΡΠ°ΡΡΠ΅ΡΠΎΠ²:
const hasPositionChanged = ({ pos, prevPos }) => pos !== prevPos; const valueInRange = ({ minScale, maxScale, scale }) => scale <= maxScale && scale >= minScale; const getTranslate = ({ minScale, maxScale, scale }) => ({ pos, prevPos, translate }) => valueInRange({ minScale, maxScale, scale }) && hasPositionChanged({ pos, prevPos }) ? translate + (pos - prevPos * scale) * (1 - 1 / scale) : translate; const getScale = ({ scale, minScale, maxScale, scaleSensitivity, deltaScale }) => { let newScale = scale + (deltaScale / (scaleSensitivity / scale)); newScale = Math.max(minScale, Math.min(newScale, maxScale)); return [scale, newScale]; };
ΠΠ΅ΡΠΎΠ΄ getScale
ΡΠ°ΡΡΡΠΈΡΡΠ²Π°Π΅Ρ Π½ΠΎΠ²ΡΠΉ ΠΌΠ°ΡΡΡΠ°Π± Π½Π° ΠΎΡΠ½ΠΎΠ²Π΅ ΠΏΡΠ΅Π΄ΡΠ΄ΡΡΠ΅Π³ΠΎ Π·Π½Π°ΡΠ΅Π½ΠΈΡ, ΠΌΠΈΠ½ΠΈΠΌΠ°Π»ΡΠ½ΠΎΠ³ΠΎ ΠΈ ΠΌΠ°ΠΊΡΠΈΠΌΠ°Π»ΡΠ½ΠΎΠ³ΠΎ ΠΎΠ³ΡΠ°Π½ΠΈΡΠ΅Π½ΠΈΠΉ (minScale
, maxScale
) ΠΈ ΠΊΠΎΡΡΡΠΈΡΠΈΠ΅Π½ΡΠΎΠ² (scaleSensitivity
, deltaScale
).
ΠΠ΅ΡΠΎΠ΄ getTranslate
ΡΠ°ΡΡΡΠΈΡΡΠ²Π°Π΅Ρ Π½ΠΎΠ²ΡΠΉ ΡΠ΄Π²ΠΈΠ³ Π½Π° ΠΎΡΠ½ΠΎΠ²Π΅ ΠΌΠ°ΡΡΡΠ°Π±Π° ΠΈ ΡΠ΅ΠΊΡΡΠ΅ΠΉ ΠΈ ΠΏΡΠ΅Π΄ΡΠ΄ΡΡΠ΅ΠΉ ΠΏΠΎΠ·ΠΈΡΠΈΠΈ.
Π Π²ΠΎΡ ΠΈ ΡΠ΅Π°Π»ΠΈΠ·Π°ΡΠΈΡ ΡΡΠ½ΠΊΡΠΈΠΈ makeZoom
:
const makeZoom = (state) => ({ zoom: ({ x, y, deltaScale }) => { const { left, top } = state.element.getBoundingClientRect(); const { minScale, maxScale, scaleSensitivity } = state; const [ scale, newScale ] = getScale({ scale: state.transformation.scale, deltaScale, minScale, maxScale, scaleSensitivity }); const originX = x - left; const originY = y - top; const newOriginX = originX / scale; const newOriginY = originY / scale; const translate = getTranslate({ scale, minScale, maxScale }); const translateX = translate({ pos: originX, prevPos: state.transformation.originX, translate: state.transformation.translateX }); const translateY = translate({ pos: originY, prevPos: state.transformation.originY, translate: state.transformation.translateY }); state.element.style.transformOrigin = `${newOriginX}px ${newOriginY}px`; state.element.style.transform = getMatrix({ scale: newScale, translateX, translateY }); state.transformation = { originX: newOriginX, originY: newOriginY, translateX, translateY, scale: newScale }; } });
ΠΠ½Π° Π²ΠΎΠ·Π²ΡΠ°ΡΠ°Π΅Ρ ΡΠΎΠ»ΡΠΊΠΎ ΠΎΠ΄ΠΈΠ½ ΠΌΠ΅ΡΠΎΠ΄ zoom
, ΠΏΡΠ΅Π΄Π½Π°Π·Π½Π°ΡΠ΅Π½Π½ΡΠΉ Π΄Π»Ρ ΠΌΠ°ΡΡΡΠ°Π±ΠΈΡΠΎΠ²Π°Π½ΠΈΡ ΡΠ»Π΅ΠΌΠ΅Π½ΡΠ°. ΠΠ½ ΠΏΠΎΠ»ΡΡΠ°Π΅Ρ ΠΊΠΎΠΎΡΠ΄ΠΈΠ½Π°ΡΡ ΠΊΡΡΡΠΎΡΠ°, Π° ΡΠ°ΠΊΠΆΠ΅ ΠΏΠ°ΡΠ°ΠΌΠ΅ΡΡ deltaScale
β ΠΊΠΎΡΡΡΠΈΡΠΈΠ΅Π½Ρ, ΠΊΠΎΡΠΎΡΡΠΉ ΠΎΠΏΡΠ΅Π΄Π΅Π»ΡΠ΅Ρ Π½Π°ΠΏΡΠ°Π²Π»Π΅Π½ΠΈΠ΅ ΠΌΠ°ΡΡΡΠ°Π±ΠΈΡΠΎΠ²Π°Π½ΠΈΡ (1
Π΄Π»Ρ ΡΠ²Π΅Π»ΠΈΡΠ΅Π½ΠΈΡ, -1
Π΄Π»Ρ ΡΠΌΠ΅Π½ΡΡΠ΅Π½ΠΈΡ).
Π€ΡΠ½ΠΊΡΠΈΡ Π²ΡΡΠΈΡΠ»ΡΠ΅Ρ Π½ΠΎΠ²ΡΠ΅ ΠΏΠ°ΡΠ°ΠΌΠ΅ΡΡΡ ΡΡΠ°Π½ΡΡΠΎΡΠΌΠ°ΡΠΈΠΈ ΠΈ ΠΎΠ±Π½ΠΎΠ²Π»ΡΠ΅Ρ ΡΠ²ΠΎΠΉΡΡΠ²ΠΎ style
ΡΠ»Π΅ΠΌΠ΅Π½ΡΠ°.
ΠΡΠΈ ΠΌΠ°ΡΡΡΠ°Π±ΠΈΡΠΎΠ²Π°Π½ΠΈΠΈ ΠΊΡΠΎΠΌΠ΅ style.transform
Π½ΡΠΆΠ½ΠΎ ΠΈΠ·ΠΌΠ΅Π½ΡΡΡ ΡΠ°ΠΊΠΆΠ΅ ΡΠ²ΠΎΠΉΡΡΠ²ΠΎ style.transformOrigin
, ΡΡΠΎΠ±Ρ ΡΠΊΠΎΡΡΠ΅ΠΊΡΠΈΡΠΎΠ²Π°ΡΡ ΠΏΠΎΠ·ΠΈΡΠΈΡ ΡΠ»Π΅ΠΌΠ΅Π½ΡΠ°. Π ΠΊΠ°ΡΠ΅ΡΡΠ²Π΅ ΡΠΊΡΠΏΠ΅ΡΠΈΠΌΠ΅Π½ΡΠ° Π²Ρ ΠΌΠΎΠΆΠ΅ΡΠ΅ Π·Π°ΠΊΠΎΠΌΠΌΠ΅Π½ΡΠΈΡΠΎΠ²Π°ΡΡ 14 ΡΡΡΠΎΡΠΊΡ ΠΈ ΠΏΠΎΡΠΌΠΎΡΡΠ΅ΡΡ, ΡΡΠΎ Π±ΡΠ΄Π΅Ρ.
ΠΠ»Π°Π²Π½ΡΠΉ ΡΠ°ΠΉΠ»
ΠΡΠΎΠΌΠ΅ ΡΠΎΠ³ΠΎ ΠΌΡ ΡΠ΄Π΅Π»Π°Π΅ΠΌ Π³Π»Π°Π²Π½ΡΠΉ ΡΠ°ΠΉΠ» ΠΏΡΠΈΠ»ΠΎΠΆΠ΅Π½ΠΈΡ index.js
:
(() => { const { renderer } = require("./src/renderer"); const container = document.getElementById("container"); const instance = renderer({ minScale: .1, maxScale: 30, element: container.children[0], scaleSensitivity: 50 }); container.addEventListener("wheel", (event) => { if (!event.ctrlKey) { return; } event.preventDefault(); instance.zoom({ deltaScale: Math.sign(event.deltaY) > 0 ? 1 : -1, x: event.pageX, y: event.pageY }); }); container.addEventListener("dblclick", () => { instance.panTo({ originX: 0, originY: 0, scale: 1, }); }); container.addEventListener("mousemove", (event) => { if (!event.shiftKey) { return; } event.preventDefault(); instance.panBy({ originX: event.movementX, originY: event.movementY }); }) })();
ΠΠ»ΠΈΠ΅Π½ΡΡΠΊΠΈΠΉ ΠΊΠΎΠ΄ ΡΠΎΠ·Π΄Π°Π΅Ρ ΡΠΊΠ·Π΅ΠΌΠΏΠ»ΡΡ renderer
ΠΈ ΠΏΠ΅ΡΠ΅Π΄Π°Π΅Ρ Π΅ΠΌΡ Π±Π°Π·ΠΎΠ²ΡΡ ΠΊΠΎΠ½ΡΠΈΠ³ΡΡΠ°ΡΠΈΡ:
- ΡΠ»Π΅ΠΌΠ΅Π½Ρ, ΡΠ°Π·ΠΌΠ΅Ρ ΠΊΠΎΡΠΎΡΠΎΠ³ΠΎ Π±ΡΠ΄Π΅Ρ ΠΈΠ·ΠΌΠ΅Π½ΡΡΡΡΡ;
- ΠΌΠΈΠ½ΠΈΠΌΠ°Π»ΡΠ½ΡΠΉ ΠΈ ΠΌΠ°ΠΊΡΠΈΠΌΠ°Π»ΡΠ½ΡΠΉ ΠΌΠ°ΡΡΡΠ°Π±;
- ΠΊΠΎΡΡΡΠΈΡΠΈΠ΅Π½Ρ ΡΡΠ²ΡΡΠ²ΠΈΡΠ΅Π»ΡΠ½ΠΎΡΡΠΈ ΠΌΠ°ΡΡΡΠ°Π±ΠΈΡΠΎΠ²Π°Π½ΠΈΡ.
ΠΠ°ΡΠ΅ΠΌ ΡΡΡΠ°Π½Π°Π²Π»ΠΈΠ²Π°ΡΡΡΡ ΡΠ»ΡΡΠ°ΡΠ΅Π»ΠΈ ΡΠΎΠ±ΡΡΠΈΠΉ ΠΌΡΡΠΈ ΠΈ Π² Π½ΡΠΆΠ½ΡΠΉ ΠΌΠΎΠΌΠ΅Π½Ρ Π²ΡΠ·ΡΠ²Π°ΡΡΡΡ Π½ΡΠΆΠ½ΡΠ΅ ΠΌΠ΅ΡΠΎΠ΄Ρ:
- Π΄Π»Ρ ΠΌΠ°ΡΡΡΠ°Π±ΠΈΡΠΎΠ²Π°Π½ΠΈΡ ΠΈΡΠΏΠΎΠ»ΡΠ·ΡΠΉΡΠ΅ ΠΊΠΎΠ»Π΅ΡΠΈΠΊΠΎ ΠΌΡΡΠΈ ΠΈΠ»ΠΈ ΡΠ΅Π½ΡΠΎΡΠ½ΡΡ ΠΏΠ°Π½Π΅Π»Ρ, Π·Π°ΠΆΠ°Π² ΠΊΠ»Π°Π²ΠΈΡΡ CTRL.
- Π΄Π»Ρ ΠΏΠ΅ΡΠ΅ΠΌΠ΅ΡΠ΅Π½ΠΈΡ β ΠΏΠ΅ΡΠ΅ΠΌΠ΅ΡΠ°ΠΉΡΠ΅ ΠΌΡΡΡ ΠΈΠ»ΠΈ ΠΈΡΠΏΠΎΠ»ΡΠ·ΡΠΉΡΠ΅ ΡΠ°ΡΠΏΠ°Π΄, Π·Π°ΠΆΠ°Π² ΠΊΠ»Π°Π²ΠΈΡΡ SHIFT.
- ΠΠ²ΠΎΠΉΠ½ΠΎΠΉ ΡΠ΅Π»ΡΠΎΠΊ ΠΌΡΡΠΈ Π²ΠΎΡΡΡΠ°Π½ΠΎΠ²ΠΈΡ ΠΈΡΡ ΠΎΠ΄Π½ΠΎΠ΅ ΡΠΎΡΡΠΎΡΠ½ΠΈΠ΅ ΡΠ»Π΅ΠΌΠ΅Π½ΡΠ°.
ΠΠ΅ΠΌΠΎ-ΠΏΡΠΈΠΌΠ΅Ρ:
Π’Π΅ΡΡΠΈΡΠΎΠ²Π°Π½ΠΈΠ΅
ΠΡΠΎΠ²Π΅ΡΠΈΠΌ ΡΠ΅Π°Π»ΠΈΠ·ΠΎΠ²Π°Π½Π½ΡΠ΅ ΡΡΠ½ΠΊΡΠΈΠΈ Ρ ΠΏΠΎΠΌΠΎΡΡΡ Π±ΠΈΠ±Π»ΠΈΠΎΡΠ΅ΠΊΠΈ Mocha:
const panByTestCases = [ { description: 'should pan by passed originX and originY (x: 100, y: 100)', minScale: .1, maxScale: 20, origins: [ { originX: 100, originY: 100, } ], result: 'matrix(1, 0, 0, 1, 100, 100)' }, { description: 'should pan by passed originX and originY (x: 50, y: 50)', minScale: .1, maxScale: 20, origins: [ { originX: 100, originY: 100, }, { originX: -50, originY: -50, } ], result: 'matrix(1, 0, 0, 1, 50, 50)' }, { description: 'should pan by passed originX and originY (x: -50, y: 50)', minScale: .1, maxScale: 20, origins: [ { originX: -100, originY: 100, }, { originX: 50, originY: -50, } ], result: 'matrix(1, 0, 0, 1, -50, 50)' } ]; module.exports = { panByTestCases, };
const assert = require('assert'); const { renderer } = require('../src/renderer'); const { panByTestCases } = require('./renderer.testCases'); describe('renderer', () => { let _element; beforeEach(() => { _element = { getBoundingClientRect: () => ({ left: 0, top: 0 }), style: { transform: "", transformOrigin: "", } } }); describe('#canPan()', () => { describe('#panBy()', () => { panByTestCases.forEach(({ description, minScale, maxScale, origins, result }) => it(description, () => { const instance = renderer({ minScale, maxScale, element: _element }) origins.forEach(({ originX, originY }) => instance.panBy({ originX, originY })) assert.equal(_element.style.transform, result); }), ); }); });
ΠΠ΅ΡΠ΅Π΄ ΠΊΠ°ΠΆΠ΄ΡΠΌ ΡΠ΅ΡΡΠΎΠΌ (beforeEach
) ΡΠΎΠ·Π΄Π°Π΅ΡΡΡ ΠΎΠ±ΡΠ΅ΠΊΡ _element
Ρ Π΄Π΅ΡΠΎΠ»ΡΠ½ΡΠΌΠΈ Π·Π½Π°ΡΠ΅Π½ΠΈΡΠΌΠΈ.
ΠΠ΅ΠΉΡΡ ΡΠ΅ΡΡΠΈΡΠΎΠ²Π°Π½ΠΈΡ Π΄Π»Ρ ΡΠ΄ΠΎΠ±ΡΡΠ²Π° Π²ΡΠ½Π΅ΡΠ΅Π½Ρ Π² ΠΎΡΠ΄Π΅Π»ΡΠ½ΡΠΉ ΡΠ°ΠΉΠ» renderer.testCases.js
.
Π ΠΈΡΠΎΠ³Π΅ Ρ Π½Π°Ρ ΠΏΠΎΠ»ΡΡΠΈΠ»ΡΡ ΠΎΡΠ΅Π½Ρ ΠΏΡΠΎΡΡΠΎΠΉ ΠΈ ΡΠ΄ΠΎΠ±Π½ΡΠΉ ΠΈΠ½ΡΡΡΡΠΌΠ΅Π½Ρ Π΄Π»Ρ ΠΌΠ°ΡΡΡΠ°Π±ΠΈΡΠΎΠ²Π°Π½ΠΈΡ ΠΈ ΠΏΠ°Π½ΠΎΡΠ°ΠΌΠΈΡΠΎΠ²Π°Π½ΠΈΡ Π½Π° JavaScript, ΡΠΎΡΡΠΎΡΡΠΈΠΉ Π²ΡΠ΅Π³ΠΎ ΠΈΠ· 69 ΡΡΡΠΎΠΊ ΠΊΠΎΠ΄Π°. ΠΠ³ΠΎ ΠΌΠΎΠΆΠ½ΠΎ ΡΠΎΠΊΡΠ°ΡΠΈΡΡ Π±ΠΎΠ»ΡΡΠ΅, Π½ΠΎ Π½Π΅ Ρ ΠΎΡΠ΅ΡΡΡ ΡΠ΅ΡΡΡΡ ΡΠΈΡΠ°Π±Π΅Π»ΡΠ½ΠΎΡΡΡ.
ΠΠΎΠ»Π½ΡΡ ΠΈ ΠΌΠΈΠ½ΠΈΡΠΈΡΠΈΡΠΎΠ²Π°Π½Π½ΡΡ Π²Π΅ΡΡΠΈΠΈ ΠΈΡΠΈΡΠ΅ Π² ΡΠ΅ΠΏΠΎΠ·ΠΈΡΠΎΡΠΈΠΈ ΠΏΡΠΎΠ΅ΠΊΡΠ°.