ReactSVG에서 Panning, Zoom In/Out 사용해보기

FGPRJS·2021년 11월 27일
0

Panning 구현하기

Panning을 구현해본다.

다음 링크를 참조해본다.

상기 링크에서는 다음과 같이 이벤트를 할당한다.

// We select the SVG into the page
var svg = document.querySelector('svg');

// If browser supports pointer events
if (window.PointerEvent) {
  svg.addEventListener('pointerdown', onPointerDown); // Pointer is pressed
  svg.addEventListener('pointerup', onPointerUp); // Releasing the pointer
  svg.addEventListener('pointerleave', onPointerUp); // Pointer gets out of the SVG area
  svg.addEventListener('pointermove', onPointerMove); // Pointer is moving
} else {
  // Add all mouse events listeners fallback
  svg.addEventListener('mousedown', onPointerDown); // Pressing the mouse
  svg.addEventListener('mouseup', onPointerUp); // Releasing the mouse
  svg.addEventListener('mouseleave', onPointerUp); // Mouse gets out of the SVG area
  svg.addEventListener('mousemove', onPointerMove); // Mouse is moving

  // Add all touch events listeners fallback
  svg.addEventListener('touchstart', onPointerDown); // Finger is touching the screen
  svg.addEventListener('touchend', onPointerUp); // Finger is no longer touching the screen
  svg.addEventListener('touchmove', onPointerMove); // Finger is moving
}

주요 요점으로는, windowPointerEvent를 통하여 장치가 포인터를 사용하는지, 마우스나 터치를 사용하는지 구분할 수 있다는 점이다.

ReactSVG를 사용하여, svg의 beforeInjection(DOM에 로드되기 직전에 호출됨)에서 다음과 같이 설정할 수 있다.

beforeInjection = {(svg) => {

                if (window.PointerEvent) {
                    svg.addEventListener('pointerdown', onPointerDown); // Pointer is pressed
                    svg.addEventListener('pointerup', onPointerUp); // Releasing the pointer
                    svg.addEventListener('pointerleave', onPointerUp); // Pointer gets out of the SVG area
                    svg.addEventListener('pointermove', onPointerMove); // Pointer is moving
                  } else {
                    // Add all mouse events listeners fallback
                    svg.addEventListener('mousedown', onPointerDown); // Pressing the mouse
                    svg.addEventListener('mouseup', onPointerUp); // Releasing the mouse
                    svg.addEventListener('mouseleave', onPointerUp); // Mouse gets out of the SVG area
                    svg.addEventListener('mousemove', onPointerMove); // Mouse is moving
                  
                    // Add all touch events listeners fallback
                    svg.addEventListener('touchstart', onPointerDown); // Finger is touching the screen
                    svg.addEventListener('touchend', onPointerUp); // Finger is no longer touching the screen
                    svg.addEventListener('touchmove', onPointerMove); // Finger is moving
                  }
            }}

Offset을 계산하기 위하여 viewBox객체를 독립시킨다.

//예시
var viewBox = {
  x: 0,
  y: 0,
  width: 500,
  height: 500
};

//변형 

class ViewBox {
    constructor(x, y, width, height){
        this.x = x;
        this.y = y;
        this.width = width;
        this.height = height;
    }

    getString(){
        return [this.x, this.y, this.width, this.height].join(' ')
    }
}

최종적으로, 다음과 같이 구현하였다.

import kr_map from './kr.svg'
import React from 'react'
import { ReactSVG } from 'react-svg'

class ViewBox {
    constructor(x, y, width, height){
        this.x = x;
        this.y = y;
        this.width = width;
        this.height = height;
    }

    getString(){
        return [this.x, this.y, this.width, this.height].join(' ')
    }
}

export default class map extends React.Component {
    constructor(props){
        super(props);

        this.current_viewbox = new ViewBox(0,0,1200,1080);
        this.new_viewbox = new ViewBox(0,0,1200,1080);
        this.pointerOrigin = new ViewBox(0,0,0,0);
        this.isPointerDown = false;
        this.svg = null;
    }

    // This function returns an object with X & Y values from the pointer event
    getPointFromEvent (event) {
        var point = {x:0, y:0};
        // If event is triggered by a touch event, we get the position of the first finger
        if (event.targetTouches) {
        point.x = event.targetTouches[0].clientX;
        point.y = event.targetTouches[0].clientY;
        } else {
        point.x = event.clientX;
        point.y = event.clientY;
        }
        
        return point;
    }

    onPointerUp() {
        // The pointer is no longer considered as down
        this.isPointerDown = false;
      
        // We save the viewBox coordinates based on the last pointer offsets
        this.current_viewbox.x = this.new_viewbox.x;
        this.current_viewbox.y = this.new_viewbox.y;
      }

    // Function called by the event listeners when user start pressing/touching
    onPointerDown(event) {
        this.isPointerDown = true; // We set the pointer as down
        
        // We get the pointer position on click/touchdown so we can get the value once the user starts to drag
        var pointerPosition = this.getPointFromEvent(event);
        this.pointerOrigin.x = pointerPosition.x;
        this.pointerOrigin.y = pointerPosition.y;
    }

     // Function called by the event listeners when user start moving/dragging
    onPointerMove (event) {
        // Only run this function if the pointer is down
        if (!this.isPointerDown) {
        return;
        }
        // This prevent user to do a selection on the page
        event.preventDefault();

        // Get the pointer position
        var pointerPosition = this.getPointFromEvent(event);

        // We calculate the distance between the pointer origin and the current position
        // The viewBox x & y values must be calculated from the original values and the distances
        this.new_viewbox.x = this.current_viewbox.x - (pointerPosition.x - this.pointerOrigin.x);
        this.new_viewbox.y = this.current_viewbox.y - (pointerPosition.y - this.pointerOrigin.y);

        // We create a string with the new viewBox values
        // The X & Y values are equal to the current viewBox minus the calculated distances
        var viewBoxString = `${this.new_viewbox.x} ${this.new_viewbox.y} ${this.current_viewbox.width} ${this.current_viewbox.height}`;
        // We apply the new viewBox values onto the SVG
        this.svg.setAttribute('viewBox', viewBoxString);
    }


    render(){
       return <ReactSVG
            beforeInjection = {(svg) => {
                this.svg = svg;
                svg.setAttribute('width','1000');
                svg.setAttribute('height','1300');
                svg.setAttribute('viewBox','0 0 1200 1080');

                if (window.PointerEvent) {
                    svg.addEventListener('pointerdown', this.onPointerDown.bind(this)); // Pointer is pressed
                    svg.addEventListener('pointerup', this.onPointerUp.bind(this)); // Releasing the pointer
                    svg.addEventListener('pointerleave', this.onPointerUp.bind(this)); // Pointer gets out of the SVG area
                    svg.addEventListener('pointermove', this.onPointerMove.bind(this)); // Pointer is moving
                  } else {
                    // Add all mouse events listeners fallback
                    svg.addEventListener('mousedown', this.onPointerDown.bind(this)); // Pressing the mouse
                    svg.addEventListener('mouseup', this.onPointerUp.bind(this)); // Releasing the mouse
                    svg.addEventListener('mouseleave', this.onPointerUp.bind(this)); // Mouse gets out of the SVG area
                    svg.addEventListener('mousemove', this.onPointerMove.bind(this)); // Mouse is moving
                  
                    // Add all touch events listeners fallback
                    svg.addEventListener('touchstart', this.onPointerDown.bind(this)); // Finger is touching the screen
                    svg.addEventListener('touchend', this.onPointerUp.bind(this)); // Finger is no longer touching the screen
                    svg.addEventListener('touchmove', this.onPointerMove.bind(this)); // Finger is moving
                  }
            }}

            afterInjection = {(error, svg) => {
                if (error) {
                    console.error(error);
                    return;
                }
                svg.classList.add('region');
                console.log(svg.classList);
            }
            }

            src = {kr_map}
       ></ReactSVG>
    }
}

Panning 코드 결과물


Zoom In/Out 구현하기

상기 기재된 코드에 다음 함수를 추가한다.

onZoom(event){

  if(event.deltaY > 0){
    this.current_viewbox.width /= .9;
    this.current_viewbox.height /= .9;
  }
  else if(event.deltaY < 0){
    this.current_viewbox.width /= 1.10;
    this.current_viewbox.height /=1.10;
  }


  // this.current_viewbox.x -= (this.current_viewbox.width - tempViewBoxWidth) / 2;
  // this.current_viewbox.y -= (this.current_viewbox.height - tempViewBoxHeight) / 2; 

  var viewBoxString = `${this.current_viewbox.x} ${this.current_viewbox.y} ${this.current_viewbox.width} ${this.current_viewbox.height}`;
  this.svg.setAttribute('viewBox', viewBoxString);
}

상기 이벤트를 받을 wheel의 zoom을 추가한다.

beforeInjection = {(svg) => {
                this.svg = svg;
                svg.setAttribute('width','1000');
                svg.setAttribute('height','1300');
                svg.setAttribute('viewBox','0 0 1200 1080');

                if (window.PointerEvent) {
                    svg.addEventListener('pointerdown', this.onPointerDown.bind(this)); // Pointer is pressed
                    svg.addEventListener('pointerup', this.onPointerUp.bind(this)); // Releasing the pointer
                    svg.addEventListener('pointerleave', this.onPointerUp.bind(this)); // Pointer gets out of the SVG area
                    svg.addEventListener('pointermove', this.onPointerMove.bind(this)); // Pointer is moving
                    svg.addEventListener('wheel', this.onZoom.bind(this));
                } else {
                    // Add all mouse events listeners fallback
                    svg.addEventListener('mousedown', this.onPointerDown.bind(this)); // Pressing the mouse
                    svg.addEventListener('mouseup', this.onPointerUp.bind(this)); // Releasing the mouse
                    svg.addEventListener('mouseleave', this.onPointerUp.bind(this)); // Mouse gets out of the SVG area
                    svg.addEventListener('mousemove', this.onPointerMove.bind(this)); // Mouse is moving

                    // Add all touch events listeners fallback
                    svg.addEventListener('touchstart', this.onPointerDown.bind(this)); // Finger is touching the screen
                    svg.addEventListener('touchend', this.onPointerUp.bind(this)); // Finger is no longer touching the screen
                    svg.addEventListener('touchmove', this.onPointerMove.bind(this)); // Finger is moving
                  }
            }}

Zoom 결과물


profile
FGPRJS

0개의 댓글