import { TouchController, MultiTouchController, Vector2, svgTransformationDecode, svgTransformationEncode, DebugLayer, } from 'touchcontroller'; import { moveSvgGBy } from '../tools/svgGTools'; import { DisableSelect } from './../tools/DisableSelect'; import { Rect } from './Rect'; export class PlaygroundActive { public rects: Rect[]; public debugCanvasElementCtx: CanvasRenderingContext2D | null = null; public offset: Vector2; public initialScroll: Vector2; private playgroundDraggingLayerSvg: SVGSVGElement; constructor( public svgElement: SVGElement | HTMLElement, //todo what is propper type to HTMLSVGELement? public anchorElement: HTMLElement, //public touchcontroller: touchcontroller, public debugLayer: { canvas: HTMLCanvasElement; offset: Vector2; } | null = null, private getScroll: () => Vector2, ) { this.rects = []; if (debugLayer) { this.debugCanvasElementCtx = debugLayer.canvas.getContext('2d'); this._renderLoopTick(); } this.initialScroll = this.getScroll(); this._initializeTouches(!!debugLayer); this._parseSvg(svgElement); const playgroundDraggingLayer = document.createElement('DIV'); playgroundDraggingLayer.style.zIndex = '9999999999999'; playgroundDraggingLayer.style.position = 'fixed'; playgroundDraggingLayer.style.pointerEvents = 'none'; //playgroundDraggingLayer.style.backgroundColor = '#906090'; playgroundDraggingLayer.style.top = '0'; playgroundDraggingLayer.style.left = '0'; playgroundDraggingLayer.style.right = '0'; playgroundDraggingLayer.style.bottom = '0'; document.body.appendChild(playgroundDraggingLayer); const playgroundDraggingLayerSvg = document.createElement('SVG'); playgroundDraggingLayer.appendChild(playgroundDraggingLayerSvg); playgroundDraggingLayerSvg.setAttribute( 'width', playgroundDraggingLayer.getBoundingClientRect().width.toString(), ); playgroundDraggingLayerSvg.setAttribute( 'height', playgroundDraggingLayer.getBoundingClientRect().height.toString(), ); //this.playgroundDraggingLayerSvg = playgroundDraggingLayerSvg;//window.document.querySelector('#playground-dragging-layer svg') as SVGSVGElement; //todo this is weird hack see playgroundDraggingLayer.innerHTML = playgroundDraggingLayer.innerHTML; this.playgroundDraggingLayerSvg = playgroundDraggingLayer.querySelector( 'svg', ) as SVGSVGElement; } private getScrollDelta() { return this.initialScroll.subtract(this.getScroll()); } private _parseSvg(svgElement: SVGElement | HTMLElement) { //console.log('parsing svg',svgElement); const boundingBoxAll = svgElement.getBoundingClientRect(); //todo to rect this.offset = new Vector2(boundingBoxAll.left, boundingBoxAll.top); for (const groupElement of Array.prototype.slice.call( svgElement.querySelectorAll('g'), )) { this.addSvgGroup(svgElement as SVGGElement /*, null*/); } } addSvgGroup( groupElement: SVGGElement /*, immediateDrag: Event | null = null*/, ) { const transformOriginal = svgTransformationDecode( groupElement.getAttribute('transform') || undefined, ); const transformZeroRotation = transformOriginal.clone(); transformZeroRotation.rotate = 0; groupElement.setAttribute( 'transform', svgTransformationEncode(transformZeroRotation), ); const boundingBoxZeroRotate = groupElement.getBoundingClientRect(); //todo to rect groupElement.setAttribute( 'transform', svgTransformationEncode(transformOriginal), ); const boundingBoxWithRotate = groupElement.getBoundingClientRect(); //todo to rect this.addRect( new Rect( 'transparent', //'rgba(1,0,0,0.5)', groupElement, new Vector2( (boundingBoxWithRotate.right + boundingBoxWithRotate.left) / 2, (boundingBoxWithRotate.bottom + boundingBoxWithRotate.top) / 2, ) .subtractInPlace(this.getScrollDelta()) .subtractInPlace(this.offset), new Vector2( boundingBoxZeroRotate.right - boundingBoxZeroRotate.left, boundingBoxZeroRotate.bottom - boundingBoxZeroRotate.top, ), transformOriginal.rotate, this, ), //immediateDrag, ); } addRect(rect: Rect /*, immediateDrag: Event | null = null*/) { //console.log(rect); this.rects.push(rect); this.touchController.addElement( rect.svgGroupElement /*, immediateDrag*/, ); } public touchController: TouchController; private _initializeTouches(debug: boolean) { //console.log(this.rects); //console.log(this.rects.map((rect)=>rect.svgElement)[0]); this.touchController = new TouchController( [], //this.rects.map((rect) => rect.svgElement), document.body, ); const multiTouchController = new MultiTouchController( this.touchController, (frame) => this.rects.find( (rect) => rect.svgGroupElement === frame.element, ), ); if (debug) { new DebugLayer(multiTouchController); } //console.log(multiTouchController); multiTouchController.hoveredElementsChanges.subscribe( ([elementNew, elementOld]) => { console.log([elementNew, elementOld]); if (elementNew || false) { elementNew.hovered = true; } if (elementOld || false) { elementOld.hovered = false; } }, ); multiTouchController.multiTouches.subscribe((multitouch) => { // console.log('multitouch', multitouch); if (typeof multitouch.element === 'undefined') return; const rect = multitouch.element; const sizeRatio = rect.size.y / rect.size.x; // rect.svgGroupElement const rectSvgElementOnDraggingLayer = rect.svgGroupElement.cloneNode( true, ) as SVGGElement; this.playgroundDraggingLayerSvg.appendChild( rectSvgElementOnDraggingLayer, ); rectSvgElementOnDraggingLayer.style.opacity = '0.5'; //todo as const in config moveSvgGBy(rectSvgElementOnDraggingLayer, this.getScrollDelta()); rect.startTransformations(); const disableSelect = new DisableSelect(); multitouch .transformations( /*new Transformation( rect.center, rect.rotation, rect.size.x )*/ rect, ) .subscribe( (transformation) => { rectSvgElementOnDraggingLayer.setAttribute( 'transform', rect.svgGroupElement.getAttribute('transform')!, ); moveSvgGBy( rectSvgElementOnDraggingLayer, this.getScrollDelta(), ); disableSelect.tick(); //console.log('transformation',transformation); /*multitouch.element.center = transformation.translate; multitouch.element.rotation = transformation.rotate; multitouch.element.size.x = transformation.scale; multitouch.element.size.y = transformation.scale * sizeRatio;*/ //multitouch.element.position = multitouch.element.position.add(transformation.translate); }, () => {}, () => { if (!multitouch.element) { return; } if (multitouch.empty) { //alert(`You have selected ${multitouch.element.color} rectangle.`); rect.tap(); } rect.endTransformations(); this.subscribersCall(); rectSvgElementOnDraggingLayer.remove(); disableSelect.remove(); }, ); }); } private _renderLoopTick() { if (this.debugCanvasElementCtx) { this.debugCanvasElementCtx.clearRect( 0, 0, this.debugCanvasElementCtx.canvas.width, this.debugCanvasElementCtx.canvas.height, ); for (const rect of this.rects.slice().reverse()) { rect.render( this.debugCanvasElementCtx, this.debugLayer!.offset, ); } for (const rect of this.rects) { rect.renderAnchors( this.debugCanvasElementCtx, this.debugLayer!.offset, ); } requestAnimationFrame(() => this._renderLoopTick()); } } private subscribers: Function[] = []; subscribe(callback: Function) { this.subscribers.push(callback); } private subscribersCall() { for (const subscriber of this.subscribers) { subscriber(); } } }