const IMAGE_RENDER_SIZE = 1000 ; function _getMousePos ( canvas , evt ) { const rect = canvas . getBoundingClientRect ( ) ; return new Vector2 ( evt . clientX - rect . left , evt . clientY - rect . top ) ; } class Vector2 { x : number ; y : number ; constructor ( x , y ) { this . x = x ; this . y = y ; } } export enum DrawMode { Single , Nine } class Rect { protected visible = true ; protected x = 0 ; protected y = 0 ; protected width : number ; protected height : number ; color : string ; public constructor ( ) { this . width = 1 ; this . height = 1 ; this . color = "rgb(255, 50, 50)" ; } public setSize ( w , h ) { this . width = w ; this . height = h ; } public isPointInside ( px , py ) { if ( px >= this . x && px <= this . x + this . width && py >= this . y && py <= this . y + this . height ) return true ; return false ; } public setCenter ( x , y ) { this . x = x - this . width / 2 ; this . y = y - this . height / 2 ; } public centerX ( ) { return this . x + this . width / 2 ; } public centerY ( ) { return this . y + this . height / 2 ; } public move ( dx , dy ) { this . x += dx ; this . y += dy ; } draw ( ctx ) { ctx . beginPath ( ) ; ctx . fillStyle = this . color ; ctx . rect ( this . x , this . y , this . width , this . height ) ; ctx . fill ( ) ; ctx . beginPath ( ) ; ctx . lineWidth = 4 ; ctx . strokeStyle = "rgb(0, 0, 0)" ; ctx . rect ( this . x , this . y , this . width , this . height ) ; ctx . stroke ( ) ; } } export class DragZoom { canvas : HTMLCanvasElement ; context : CanvasRenderingContext2D ; mousePos : Vector2 ; prevMousePos : Vector2 ; mouseDownPos : Vector2 ; mouseDragDiff : Vector2 ; zoomFactor : number ; offset : Vector2 ; panning : boolean ; panStart : SVGPoint ; rect : Rect ; image : HTMLCanvasElement ; public drawMode : DrawMode ; constructor ( canvas ) { this . canvas = canvas ; this . context = this . canvas . getContext ( "2d" ) ; this . image = null ; const self = this ; canvas . addEventListener ( "mousemove" , function ( evt ) { self . onMouseMove ( evt ) ; } ) ; canvas . addEventListener ( "mousedown" , function ( evt ) { self . onMouseDown ( evt ) ; } ) ; canvas . addEventListener ( "mouseup" , function ( evt ) { self . onMouseUp ( evt ) ; } ) ; canvas . addEventListener ( "mouseout" , function ( evt ) { self . onMouseOut ( evt ) ; } ) ; canvas . addEventListener ( "mousewheel" , function ( evt ) { self . onMouseScroll ( evt ) ; } ) ; canvas . addEventListener ( "contextmenu" , function ( evt ) { evt . preventDefault ( ) ; } ) ; canvas . addEventListener ( "resize" , function ( evt ) { } ) ; this . drawMode = DrawMode . Single ; this . zoomFactor = 0.4 ; this . offset = new Vector2 ( 0 , 0 ) ; this . mousePos = new Vector2 ( 0 , 0 ) ; this . rect = new Rect ( ) ; this . rect . setSize ( 50 , 50 ) ; this . offset = new Vector2 ( canvas . width * 0.5 , canvas . height * 0.5 ) ; } onResize ( width , height ) { this . offset = new Vector2 ( width * 0.5 , height * 0.5 ) ; } centerImage ( ) { this . offset = new Vector2 ( this . canvas . width * 0.5 , this . canvas . height * 0.5 ) ; this . zoomFactor = 0.4 ; } setImage ( image ) { this . image = image ; } getAbsPos ( ) { return new Vector2 ( this . canvas . offsetLeft , this . canvas . offsetTop ) ; } onMouseDown ( evt ) { if ( evt . button == 1 || evt . button == 2 ) { this . panning = true ; this . mouseDownPos = _getMousePos ( this . canvas , evt ) ; } this . mousePos = _getMousePos ( this . canvas , evt ) ; } onMouseUp ( evt ) { if ( evt . button == 1 || evt . button == 2 ) { this . panning = false ; } } onMouseMove ( evt ) { this . prevMousePos = this . mousePos ; this . mousePos = _getMousePos ( this . canvas , evt ) ; if ( this . panning ) { const prev = this . canvasToScene ( this . prevMousePos ) ; const cur = this . canvasToScene ( this . mousePos ) ; const diff = new Vector2 ( prev . x - cur . x , prev . y - cur . y ) ; const factor = this . zoomFactor ; this . offset . x -= diff . x * factor ; this . offset . y -= diff . y * factor ; } } onMouseScroll ( evt ) { if ( this . panning ) return ; const pos = _getMousePos ( this . canvas , evt ) ; const delta = ( < any > evt ) . wheelDelta > 0 ? 1.1 : 1.0 / 1.1 ; this . zoomFactor *= delta ; this . offset . x = pos . x - ( pos . x - this . offset . x ) * delta ; this . offset . y = pos . y - ( pos . y - this . offset . y ) * delta ; evt . preventDefault ( ) ; return false ; } onMouseOut ( evt ) { this . panning = false ; } zoom ( x , y , level ) { } draw ( ) { const ctx = this . context ; ctx . setTransform ( 1 , 0 , 0 , 1 , 0 , 0 ) ; ctx . fillStyle = "rgb(50,50,50)" ; ctx . fillRect ( 0 , 0 , this . canvas . width , this . canvas . height ) ; ctx . setTransform ( this . zoomFactor , 0 , 0 , this . zoomFactor , this . offset . x , this . offset . y ) ; const scenePos = this . canvasToScene ( this . mousePos ) ; if ( this . rect . isPointInside ( scenePos . x , scenePos . y ) ) { this . rect . color = "rgb(0, 255, 255)" ; } else { this . rect . color = "rgb(255, 50, 50)" ; } if ( this . image ) { if ( this . drawMode == DrawMode . Single ) { this . drawImage ( 0 , 0 ) ; } else { this . drawImage ( - 1 , - 1 ) ; this . drawImage ( 0 , - 1 ) ; this . drawImage ( 1 , - 1 ) ; this . drawImage ( - 1 , 0 ) ; this . drawImage ( 0 , 0 ) ; this . drawImage ( 1 , 0 ) ; this . drawImage ( - 1 , 1 ) ; this . drawImage ( 0 , 1 ) ; this . drawImage ( 1 , 1 ) ; } } } drawImage ( offsetX , offsetY ) { this . context . drawImage ( this . image , - IMAGE_RENDER_SIZE * 0.5 + offsetX * IMAGE_RENDER_SIZE , - IMAGE_RENDER_SIZE * 0.5 + offsetY * IMAGE_RENDER_SIZE , IMAGE_RENDER_SIZE , IMAGE_RENDER_SIZE ) ; } canvasToScene ( pos ) { return new Vector2 ( ( pos . x - this . offset . x ) * ( 1.0 / this . zoomFactor ) , ( pos . y - this . offset . y ) * ( 1.0 / this . zoomFactor ) ) ; } }