@heduapp/playground
Advanced tools
Comparing version 0.9.2 to 0.9.3
@@ -0,0 +0,0 @@ import * as TC from 'touchcontroller'; |
@@ -0,0 +0,0 @@ import * as TC from 'touchcontroller'; |
@@ -0,0 +0,0 @@ import * as TC from 'touchcontroller'; |
@@ -1,20 +0,19 @@ | ||
import * as TC from 'touchcontroller'; | ||
import { Rect } from './Rect'; | ||
interface IPlaygroundState { | ||
itemsIds: string[]; | ||
activeItems: Rect[]; | ||
} | ||
export declare class Playground { | ||
svgElement: SVGSVGElement; | ||
anchorElement: HTMLElement; | ||
debugCanvasElement: HTMLCanvasElement | null; | ||
private getScroll; | ||
rects: Rect[]; | ||
debugCanvasElementCtx: CanvasRenderingContext2D | null; | ||
offset: TC.Vector2; | ||
initialScroll: TC.Vector2; | ||
constructor(svgElement: SVGSVGElement, anchorElement: HTMLElement, //public touchcontroller: TC.touchcontroller, | ||
debugCanvasElement: HTMLCanvasElement | null, getScroll: () => TC.Vector2); | ||
private _parseSvg; | ||
addSvgGroup(groupElement: SVGGElement, immediateDrag?: Event | null): void; | ||
addRect(rect: Rect, immediateDrag?: Event | null): void; | ||
touchController: TC.TouchController; | ||
private _initializeTouches; | ||
private _renderLoopTick; | ||
private playgroundElement; | ||
private debugMode; | ||
private preserveOriginalSvg; | ||
constructor(playgroundElement: HTMLElement, debugMode?: boolean, preserveOriginalSvg?: boolean); | ||
private subscribers; | ||
subscribe(callback: Function): void; | ||
private subscribersCall; | ||
readonly state: IPlaygroundState; | ||
private itemsIds; | ||
private playgroundActive; | ||
private init; | ||
} | ||
export {}; |
import * as TC from 'touchcontroller'; | ||
import { Playground } from './Playground'; | ||
import { PlaygroundActive } from './PlaygroundActive'; | ||
import { Donor } from './Donor'; | ||
@@ -9,7 +9,7 @@ import { Acceptor } from './Acceptor'; | ||
svgElement: SVGGElement; | ||
playground: Playground; | ||
playgroundActive: PlaygroundActive; | ||
private _anchorPairs; | ||
hovered: boolean; | ||
playgroundData: IPlaygroundData; | ||
constructor(color: string, svgElement: SVGGElement, center: TC.Vector2, size: TC.Vector2, rotation: number, playground: Playground); | ||
constructor(color: string, svgElement: SVGGElement, center: TC.Vector2, size: TC.Vector2, rotation: number, playgroundActive: PlaygroundActive); | ||
visualFeedback(): void; | ||
@@ -16,0 +16,0 @@ acceptors: Acceptor[]; |
import { Playground } from './classes/Playground'; | ||
import { initializePlayground } from './initializePlayground'; | ||
export { initializePlayground, Playground }; | ||
export default Playground; |
@@ -1,1 +0,7 @@ | ||
export declare function initializePlayground(playgroundElement: HTMLElement, debugMode?: boolean, preserveOriginalSvg?: boolean): Promise<() => void>; | ||
export declare class Playground { | ||
private playgroundElement; | ||
private debugMode; | ||
private preserveOriginalSvg; | ||
constructor(playgroundElement: HTMLElement, debugMode?: boolean, preserveOriginalSvg?: boolean); | ||
private init; | ||
} |
@@ -0,0 +0,0 @@ export interface IPlaygroundData { |
export declare function getBoundingRectWithoutRotation(element: Element): ClientRect | DOMRect; |
import * as TC from 'touchcontroller'; | ||
export declare function getScroll(element: Element): TC.Vector2; |
import { IPlaygroundData } from './../interfaces/IPlaygroundData'; | ||
export declare function parsePlaygroundData(gElement: SVGGElement): IPlaygroundData; |
export declare function waitImmediate(): Promise<void>; |
export {}; |
{ | ||
"name": "@heduapp/playground", | ||
"version": "0.9.2", | ||
"author": "Pavol Hejný", | ||
"version": "0.9.3", | ||
"author": "H-Edu s.r.o.", | ||
"description": "Interactive SVG images for H-Edu Books.", | ||
"main": "./dist/index.js", | ||
"typings": "./dist/typings/src/index.d.ts", | ||
"private": false, | ||
"scripts": { | ||
@@ -15,2 +14,3 @@ "start": "npm run develop", | ||
"develop": "onchange \"src/**/*.{ts,tsx}\" --initial --kill --delay 500 -- webpack --config webpack.config.js --mode development", | ||
"deploy": "git push --tags", | ||
"expose": "node server.js", | ||
@@ -46,4 +46,6 @@ "prettier": "prettier --config .prettierrc --write \"src/**/*.{ts,tsx,js,css}\" \"test/**/*.{ts,tsx,js,css}\" webpack.config.js", | ||
"dependencies": { | ||
"touchcontroller": "^0.13.2" | ||
"@types/uuid": "^3.4.4", | ||
"touchcontroller": "^0.13.2", | ||
"uuid": "^3.3.2" | ||
} | ||
} |
@@ -32,3 +32,2 @@ import * as TC from 'touchcontroller'; | ||
if (this.donors.indexOf(donor) === -1) { | ||
//console.log(donor); | ||
@@ -35,0 +34,0 @@ //console.log(`snapDonor`); |
@@ -12,4 +12,6 @@ import * as TC from 'touchcontroller'; | ||
get position() { | ||
return this.relativePosition.rotate(this.rect.rotation).add(this.rect.shadowBoundingBox.topLeft); | ||
return this.relativePosition | ||
.rotate(this.rect.rotation) | ||
.add(this.rect.shadowBoundingBox.topLeft); | ||
} | ||
} |
@@ -0,191 +1,197 @@ | ||
import { Rect } from './Rect'; | ||
import { IPlaygroundData } from '../interfaces/IPlaygroundData'; | ||
import { PlaygroundActive } from './PlaygroundActive'; | ||
import * as TC from 'touchcontroller'; | ||
import { Rect } from './Rect'; | ||
import { v4 as uuidV4 } from 'uuid'; | ||
import { getBoundingRectWithoutRotation } from '../tools/getBoundingRectWithoutRotation'; | ||
import { getScroll } from '../tools/getScroll'; | ||
import { parsePlaygroundData } from '../tools/parsePlaygroundData'; | ||
import { waitImmediate } from '../tools/wait'; | ||
import { createPlaygroundStyles } from '../tools/createPlaygroundStyles'; | ||
//import { translateToVector, vectorToTranslate } from './svgTools'; | ||
interface IPlaygroundState { | ||
itemsIds: string[]; | ||
activeItems: Rect[]; | ||
} | ||
export class Playground { | ||
public rects: Rect[]; | ||
public debugCanvasElementCtx: CanvasRenderingContext2D | null = null; | ||
public offset: TC.Vector2; | ||
public initialScroll: TC.Vector2; | ||
constructor( | ||
public svgElement: SVGSVGElement, | ||
public anchorElement: HTMLElement, //public touchcontroller: TC.touchcontroller, | ||
public debugCanvasElement: HTMLCanvasElement | null = null, | ||
private getScroll: () => TC.Vector2, | ||
private playgroundElement: HTMLElement, | ||
private debugMode = false, | ||
private preserveOriginalSvg = false, | ||
) { | ||
this.rects = []; | ||
if (!playgroundElement) { | ||
throw new Error( | ||
`Can not initialize playground because playground element is null.`, | ||
); | ||
} | ||
this.init(); | ||
} | ||
if (debugCanvasElement) { | ||
this.debugCanvasElementCtx = debugCanvasElement.getContext('2d'); | ||
this._renderLoopTick(); | ||
private subscribers: Function[] = []; | ||
subscribe(callback: Function) { | ||
this.subscribers.push(callback); | ||
} | ||
private subscribersCall() { | ||
for (const subscriber of this.subscribers) { | ||
subscriber(); | ||
} | ||
} | ||
this.initialScroll = this.getScroll(); | ||
this._initializeTouches(); | ||
this._parseSvg(svgElement); | ||
get state(): IPlaygroundState { | ||
return { | ||
itemsIds: this.itemsIds, | ||
activeItems: this.playgroundActive | ||
? this.playgroundActive.rects | ||
: [], | ||
}; | ||
} | ||
private _parseSvg(svgElement: SVGElement) { | ||
//console.log('parsing svg',svgElement); | ||
//todo initialized | ||
private itemsIds: string[] = []; | ||
private playgroundActive: null | PlaygroundActive = null; | ||
private async init() { | ||
await waitImmediate(); | ||
const boundingBoxAll = svgElement.getBoundingClientRect(); //todo to rect | ||
this.offset = new TC.Vector2(boundingBoxAll.left, boundingBoxAll.top); | ||
const playgroundId = `playground-${uuidV4()}`; | ||
this.playgroundElement.classList.add(playgroundId); | ||
for (const groupElement of Array.prototype.slice.call( | ||
svgElement.querySelectorAll('g'), | ||
)) { | ||
this.addSvgGroup(svgElement as SVGGElement, null); | ||
let playgroundSvg = (document.createElement( | ||
'svg', | ||
) as any) as SVGSVGElement; //todo better | ||
const playgroungDiv = document.createElement('div'); | ||
playgroungDiv.classList.add('interactive-layer'); | ||
playgroungDiv.appendChild(playgroundSvg); | ||
playgroungDiv.innerHTML = playgroungDiv.innerHTML; //This hack shows regenerated svg. Otherwise SVG is not visible. | ||
let debugLayerCanvas: HTMLCanvasElement | null = null; | ||
if (this.debugMode) { | ||
const debugLayerDiv = document.createElement('div'); | ||
debugLayerDiv.classList.add('interactive-layer'); | ||
debugLayerCanvas = document.createElement('canvas'); | ||
debugLayerCanvas.width = this.playgroundElement.getBoundingClientRect().width; | ||
debugLayerCanvas.height = this.playgroundElement.getBoundingClientRect().height; | ||
debugLayerDiv.appendChild(debugLayerCanvas); | ||
this.playgroundElement.insertBefore( | ||
debugLayerDiv, | ||
this.playgroundElement.children[0], | ||
); | ||
} | ||
} | ||
addSvgGroup(groupElement: SVGGElement, immediateDrag: Event | null = null) { | ||
const transformOriginal = TC.svgTransformationDecode( | ||
groupElement.getAttribute('transform') || undefined, | ||
this.playgroundElement.insertBefore( | ||
playgroungDiv, | ||
this.playgroundElement.children[0], | ||
); | ||
const transformZeroRotation = transformOriginal.clone(); | ||
transformZeroRotation.rotate = 0; | ||
playgroundSvg = playgroungDiv.querySelector('svg')!; | ||
groupElement.setAttribute( | ||
'transform', | ||
TC.svgTransformationEncode(transformZeroRotation), | ||
); | ||
const boundingBoxZeroRotate = groupElement.getBoundingClientRect(); //todo to rect | ||
groupElement.setAttribute( | ||
'transform', | ||
TC.svgTransformationEncode(transformOriginal), | ||
); | ||
const boundingBoxWithRotate = groupElement.getBoundingClientRect(); //todo to rect | ||
const styleElement = createPlaygroundStyles(playgroundId); | ||
//console.log(this.anchorElement.scrollTop); | ||
const scrollDelta = this.getScroll().subtract(this.initialScroll); | ||
this.playgroundElement.appendChild(styleElement); | ||
this.addRect( | ||
new Rect( | ||
'transparent', //'rgba(1,0,0,0.5)', | ||
groupElement, | ||
new TC.Vector2( | ||
(boundingBoxWithRotate.right + boundingBoxWithRotate.left) / | ||
2, | ||
(boundingBoxWithRotate.bottom + boundingBoxWithRotate.top) / | ||
2, | ||
) | ||
.addInPlace(scrollDelta) | ||
.subtractInPlace(this.offset), | ||
new TC.Vector2( | ||
boundingBoxZeroRotate.right - boundingBoxZeroRotate.left, | ||
boundingBoxZeroRotate.bottom - boundingBoxZeroRotate.top, | ||
), | ||
transformOriginal.rotate, | ||
this, | ||
), | ||
immediateDrag, | ||
const playgroundActive = new PlaygroundActive( | ||
playgroundSvg, | ||
this.playgroundElement, | ||
debugLayerCanvas, | ||
() => getScroll(this.playgroundElement), | ||
); | ||
} | ||
addRect(rect: Rect, immediateDrag: Event | null = null) { | ||
//console.log(rect); | ||
this.rects.push(rect); | ||
this.touchController.addElement( | ||
(rect.svgElement as any) as HTMLElement, | ||
immediateDrag, | ||
); | ||
} | ||
for (const svgElement of Array.from( | ||
this.playgroundElement.querySelectorAll('svg'), | ||
)) { | ||
const moveBy = TC.Vector2.Zero(); | ||
public touchController: TC.TouchController; | ||
if (svgElement.hasAttribute('viewBox')) { | ||
const [minX, minY, width, height] = svgElement | ||
.getAttribute('viewBox')! | ||
.split(' ') | ||
.map((s) => parseFloat(s)); | ||
moveBy.addInPlace(new TC.Vector2(minX, minY)); | ||
} | ||
private _initializeTouches() { | ||
//console.log(this.rects); | ||
//console.log(this.rects.map((rect)=>rect.svgElement)[0]); | ||
for (const gElement of Array.from( | ||
svgElement.querySelectorAll('g'), //todo there should be :scope > g but IE 11 don`t support it | ||
)) { | ||
let id: null | string = null; | ||
this.touchController = new TC.TouchController( | ||
[], //this.rects.map((rect) => rect.svgElement) as any, | ||
document.body, | ||
true, | ||
); | ||
if (gElement.hasAttribute('data-playground-id')) { | ||
id = gElement.getAttribute('data-playground-id'); | ||
this.itemsIds.push(id!); | ||
} else { | ||
//do not worry | ||
// console.warn(`Element in playground do not have attribute "data-playground-id".`,gElement); | ||
} | ||
const multiTouchController = new TC.MultiTouchController( | ||
this.touchController, | ||
(frame) => | ||
this.rects.find( | ||
(rect) => | ||
(rect.svgElement as any) === (frame.element as any), | ||
), | ||
); | ||
let amount = parsePlaygroundData(gElement).amount; | ||
//console.log(multiTouchController); | ||
multiTouchController.hoveredElementsChanges.subscribe( | ||
([elementNew, elementOld]) => { | ||
//console.log([elementNew,elementOld]); | ||
if (elementNew || false) { | ||
elementNew.hovered = true; | ||
} | ||
if (elementOld || false) { | ||
elementOld.hovered = false; | ||
} | ||
}, | ||
); | ||
gElement.addEventListener('pointerdown', (event) => { | ||
//console.log(`Click on virgin element - making clone.`); | ||
multiTouchController.multiTouches.subscribe(function(multitouch) { | ||
//console.log('multitouch', multitouch); | ||
const playgroundElementBoundingRect = this.playgroundElement.getBoundingClientRect(); | ||
const topLeftAnchor = new TC.Vector2( | ||
playgroundElementBoundingRect.left - | ||
this.playgroundElement.scrollLeft, | ||
playgroundElementBoundingRect.top - | ||
this.playgroundElement.scrollTop, | ||
); | ||
const svgElementBoundingRect = svgElement.getBoundingClientRect(); | ||
const topLeft = new TC.Vector2( | ||
svgElementBoundingRect.left, | ||
svgElementBoundingRect.top, | ||
) | ||
.subtractInPlace(topLeftAnchor) | ||
.subtractInPlace(moveBy); | ||
if (typeof multitouch.element === 'undefined') return; | ||
const rect = multitouch.element; | ||
const sizeRatio = rect.size.y / rect.size.x; | ||
const gElementBoundingRect = getBoundingRectWithoutRotation( | ||
gElement, | ||
); | ||
const gElementNew = gElement.cloneNode(true) as SVGGElement; | ||
rect.startTransformations(); | ||
let transform = TC.svgTransformationDecode( | ||
gElement.getAttribute('transform') || undefined, //todo take new or old? Which is better??? | ||
); | ||
multitouch | ||
.transformations( | ||
/*new TC.Transformation( | ||
rect.center, | ||
rect.rotation, | ||
rect.size.x | ||
)*/ | ||
rect, | ||
) | ||
.subscribe( | ||
function(transformation) { | ||
//console.log(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); | ||
}, | ||
function() {}, | ||
function() { | ||
if (!multitouch.element) { | ||
return; | ||
} | ||
transform = transform.add( | ||
TC.Transformation.translate(topLeft), | ||
); //todo use inPlace | ||
transform.rotateCenter = new TC.Vector2( | ||
gElementBoundingRect.width / 2, | ||
gElementBoundingRect.height / 2, | ||
); | ||
if (multitouch.empty) { | ||
//alert(`You have selected ${multitouch.element.color} rectangle.`); | ||
rect.tap(); | ||
} | ||
gElementNew.setAttribute( | ||
'transform', | ||
TC.svgTransformationEncode(transform), | ||
); | ||
playgroundSvg.appendChild(gElementNew); | ||
rect.endTransformations(); | ||
}, | ||
); | ||
}); | ||
} | ||
playgroundActive.addSvgGroup(gElementNew, event); | ||
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); | ||
if (this.preserveOriginalSvg) { | ||
return; | ||
} | ||
if (amount < 0) { | ||
return; | ||
} | ||
if (amount > 1) { | ||
amount -= 1; | ||
return; | ||
} | ||
//console.log(`Removing virgin element.`); | ||
svgElement.removeChild(gElement); | ||
}); | ||
} | ||
for (const rect of this.rects) { | ||
rect.renderAnchors(this.debugCanvasElementCtx); | ||
} | ||
requestAnimationFrame(() => this._renderLoopTick()); | ||
} | ||
playgroundActive.subscribe(() => { | ||
this.subscribersCall(); | ||
}); | ||
this.playgroundActive = playgroundActive; | ||
} | ||
} |
@@ -6,3 +6,3 @@ import { | ||
import * as TC from 'touchcontroller'; | ||
import { Playground } from './Playground'; | ||
import { PlaygroundActive } from './PlaygroundActive'; | ||
import { Donor } from './Donor'; | ||
@@ -12,2 +12,3 @@ import { Acceptor } from './Acceptor'; | ||
import { parsePlaygroundData } from '../tools/parsePlaygroundData'; | ||
import window from '@heduapp/fake-window'; | ||
@@ -25,3 +26,3 @@ export class Rect extends TC.BoundingBox { | ||
rotation: number, | ||
public playground: Playground, | ||
public playgroundActive: PlaygroundActive, | ||
) { | ||
@@ -38,3 +39,3 @@ super(center, size, rotation); | ||
this.svgElement.style.opacity = '0.5'; | ||
setTimeout(() => { | ||
window.setTimeout(() => { | ||
this.svgElement.style.opacity = '1'; | ||
@@ -53,3 +54,3 @@ }, 500); | ||
//console.log(this); | ||
for (const rect of this.playground.rects) { | ||
for (const rect of this.playgroundActive.rects) { | ||
if (rect !== this) { | ||
@@ -363,3 +364,8 @@ this.acceptors.push( | ||
//todo optimize | ||
this.renderAnchor(ctx, anchor.position, 'ACCEPTOR',`${anchor.donors.length}/${anchor.accepts}`); | ||
this.renderAnchor( | ||
ctx, | ||
anchor.position, | ||
'ACCEPTOR', | ||
`${anchor.donors.length}/${anchor.accepts}`, | ||
); | ||
} | ||
@@ -421,4 +427,4 @@ } | ||
private rawAnchorsWithoutRotationToAnchors() { | ||
this.anchors = this.rawAnchorsWithoutRotation; | ||
this.anchors = this.rawAnchorsWithoutRotation; | ||
/* | ||
@@ -425,0 +431,0 @@ todo purge |
import { Playground } from './classes/Playground'; | ||
import { initializePlayground } from './initializePlayground'; | ||
export { initializePlayground, Playground }; | ||
export default Playground; |
@@ -1,7 +0,9 @@ | ||
export function waitImmediate():Promise<void>{ | ||
return new Promise((resolve,reject)=>{ | ||
setImmediate(()=>{ | ||
import window from '@heduapp/fake-window'; | ||
export function waitImmediate(): Promise<void> { | ||
return new Promise((resolve, reject) => { | ||
window.setImmediate(() => { | ||
resolve(); | ||
}); | ||
}) | ||
} | ||
}); | ||
} |
@@ -15,3 +15,3 @@ const path = require('path'); | ||
//externals: [nodeExternals()], | ||
target: 'web', | ||
target: 'node', | ||
module: { | ||
@@ -18,0 +18,0 @@ rules: [ |
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
Uses eval
Supply chain riskPackage uses dynamic code execution (e.g., eval()), which is a dangerous practice. This can prevent the code from running in certain environments and increases the risk that the code may contain exploits or malicious behavior.
Found 2 instances in 1 package
Minified code
QualityThis package contains minified code. This may be harmless in some cases where minified code is included in packaged libraries, however packages on npm should not minify code.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
52
423339
3
1600
4
3
+ Added@types/uuid@^3.4.4
+ Addeduuid@^3.3.2
+ Added@types/uuid@3.4.13(transitive)
+ Addeduuid@3.4.0(transitive)