Add drop shadow system with animated lift/drop during drag
DropShadowFilter applied to all ImageSprites for photos-on-a-desk feel. SelectionManager now handles object dragging with shadow lift/drop and spring-animated scale (1.0 <-> 1.03). Replaced incompatible @pixi/filter-drop-shadow v5 with pixi-filters v6 for PixiJS v8 compatibility.
This commit is contained in:
Generated
+19
-292
@@ -8,8 +8,8 @@
|
||||
"name": "refboard-frontend",
|
||||
"version": "1.0.0",
|
||||
"dependencies": {
|
||||
"@pixi/filter-drop-shadow": "^5.2.0",
|
||||
"axios": "^1.7.9",
|
||||
"pixi-filters": "^6.1.5",
|
||||
"pixi-viewport": "^6.0.3",
|
||||
"pixi.js": "^8.17.0",
|
||||
"react": "^18.3.1",
|
||||
@@ -799,132 +799,12 @@
|
||||
"@jridgewell/sourcemap-codec": "^1.4.14"
|
||||
}
|
||||
},
|
||||
"node_modules/@pixi/color": {
|
||||
"version": "7.4.3",
|
||||
"resolved": "https://registry.npmjs.org/@pixi/color/-/color-7.4.3.tgz",
|
||||
"integrity": "sha512-a6R+bXKeXMDcRmjYQoBIK+v2EYqxSX49wcjAY579EYM/WrFKS98nSees6lqVUcLKrcQh2DT9srJHX7XMny3voQ==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@pixi/colord": "^2.9.6"
|
||||
}
|
||||
},
|
||||
"node_modules/@pixi/colord": {
|
||||
"version": "2.9.6",
|
||||
"resolved": "https://registry.npmjs.org/@pixi/colord/-/colord-2.9.6.tgz",
|
||||
"integrity": "sha512-nezytU2pw587fQstUu1AsJZDVEynjskwOL+kibwcdxsMBFqPsFFNA7xl0ii/gXuDi6M0xj3mfRJj8pBSc2jCfA==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@pixi/constants": {
|
||||
"version": "7.4.3",
|
||||
"resolved": "https://registry.npmjs.org/@pixi/constants/-/constants-7.4.3.tgz",
|
||||
"integrity": "sha512-QGmwJUNQy/vVEHzL6VGQvnwawLZ1wceZMI8HwJAT4/I2uAzbBeFDdmCS8WsTpSWLZjF/DszDc1D8BFp4pVJ5UQ==",
|
||||
"license": "MIT",
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/@pixi/core": {
|
||||
"version": "7.4.3",
|
||||
"resolved": "https://registry.npmjs.org/@pixi/core/-/core-7.4.3.tgz",
|
||||
"integrity": "sha512-5YDs11faWgVVTL8VZtLU05/Fl47vaP5Tnsbf+y/WRR0VSW3KhRRGTBU1J3Gdc2xEWbJhUK07KGP7eSZpvtPVgA==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@pixi/color": "7.4.3",
|
||||
"@pixi/constants": "7.4.3",
|
||||
"@pixi/extensions": "7.4.3",
|
||||
"@pixi/math": "7.4.3",
|
||||
"@pixi/runner": "7.4.3",
|
||||
"@pixi/settings": "7.4.3",
|
||||
"@pixi/ticker": "7.4.3",
|
||||
"@pixi/utils": "7.4.3"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/pixijs"
|
||||
}
|
||||
},
|
||||
"node_modules/@pixi/extensions": {
|
||||
"version": "7.4.3",
|
||||
"resolved": "https://registry.npmjs.org/@pixi/extensions/-/extensions-7.4.3.tgz",
|
||||
"integrity": "sha512-FhoiYkHQEDYHUE7wXhqfsTRz6KxLXjuMbSiAwnLb9uG1vAgp6q6qd6HEsf4X30YaZbLFY8a4KY6hFZWjF+4Fdw==",
|
||||
"license": "MIT",
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/@pixi/filter-drop-shadow": {
|
||||
"version": "5.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@pixi/filter-drop-shadow/-/filter-drop-shadow-5.2.0.tgz",
|
||||
"integrity": "sha512-cYS2KDER7cwCu0V4VNSxTHGvzmNcEXdC9j3031YBOkUAE3+p17LMS/TAt6XeMfJV7KaPuusvXy2NFgGkv3RDbw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@pixi/filter-kawase-blur": "5.1.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@pixi/core": "^7.0.0-X"
|
||||
}
|
||||
},
|
||||
"node_modules/@pixi/filter-kawase-blur": {
|
||||
"version": "5.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@pixi/filter-kawase-blur/-/filter-kawase-blur-5.1.1.tgz",
|
||||
"integrity": "sha512-nPnJ1ChBFP+4pgFCwC0RJgHAJCetiHcQU3INH7zCdq88cFABmVmhN+wCKRNg4H7lF1EJjaXgFDkTrTreOD/bnw==",
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"@pixi/core": "^7.0.0-X"
|
||||
}
|
||||
},
|
||||
"node_modules/@pixi/math": {
|
||||
"version": "7.4.3",
|
||||
"resolved": "https://registry.npmjs.org/@pixi/math/-/math-7.4.3.tgz",
|
||||
"integrity": "sha512-/uJOVhR2DOZ+zgdI6Bs/CwcXT4bNRKsS+TqX3ekRIxPCwaLra+Qdm7aDxT5cTToDzdxbKL5+rwiLu3Y1egILDw==",
|
||||
"license": "MIT",
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/@pixi/runner": {
|
||||
"version": "7.4.3",
|
||||
"resolved": "https://registry.npmjs.org/@pixi/runner/-/runner-7.4.3.tgz",
|
||||
"integrity": "sha512-TJyfp7y23u5vvRAyYhVSa7ytq0PdKSvPLXu4G3meoFh1oxTLHH6g/RIzLuxUAThPG2z7ftthuW3qWq6dRV+dhw==",
|
||||
"license": "MIT",
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/@pixi/settings": {
|
||||
"version": "7.4.3",
|
||||
"resolved": "https://registry.npmjs.org/@pixi/settings/-/settings-7.4.3.tgz",
|
||||
"integrity": "sha512-SmGK8smc0PxRB9nr0UJioEtE9hl4gvj9OedCvZx3bxBwA3omA5BmP3CyhQfN8XJ29+o2OUL01r3zAPVol4l4lA==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@pixi/constants": "7.4.3",
|
||||
"@types/css-font-loading-module": "^0.0.12",
|
||||
"ismobilejs": "^1.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@pixi/ticker": {
|
||||
"version": "7.4.3",
|
||||
"resolved": "https://registry.npmjs.org/@pixi/ticker/-/ticker-7.4.3.tgz",
|
||||
"integrity": "sha512-tHsAD0iOUb6QSGGw+c8cyRBvxsq/NlfzIFBZLEHhWZ+Bx4a0MmXup6I/yJDGmyPCYE+ctCcAfY13wKAzdiVFgQ==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@pixi/extensions": "7.4.3",
|
||||
"@pixi/settings": "7.4.3",
|
||||
"@pixi/utils": "7.4.3"
|
||||
}
|
||||
},
|
||||
"node_modules/@pixi/utils": {
|
||||
"version": "7.4.3",
|
||||
"resolved": "https://registry.npmjs.org/@pixi/utils/-/utils-7.4.3.tgz",
|
||||
"integrity": "sha512-NO3Y9HAn2UKS1YdxffqsPp+kDpVm8XWvkZcS/E+rBzY9VTLnNOI7cawSRm+dacdET3a8Jad3aDKEDZ0HmAqAFA==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@pixi/color": "7.4.3",
|
||||
"@pixi/constants": "7.4.3",
|
||||
"@pixi/settings": "7.4.3",
|
||||
"@types/earcut": "^2.1.0",
|
||||
"earcut": "^2.2.4",
|
||||
"eventemitter3": "^4.0.0",
|
||||
"url": "^0.11.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@remix-run/router": {
|
||||
"version": "1.23.2",
|
||||
"resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.23.2.tgz",
|
||||
@@ -1342,20 +1222,6 @@
|
||||
"@babel/types": "^7.28.2"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/css-font-loading-module": {
|
||||
"version": "0.0.12",
|
||||
"resolved": "https://registry.npmjs.org/@types/css-font-loading-module/-/css-font-loading-module-0.0.12.tgz",
|
||||
"integrity": "sha512-x2tZZYkSxXqWvTDgveSynfjq/T2HyiZHXb00j/+gy19yp70PHCizM48XFdjBCWH7eHBD0R5i/pw9yMBP/BH5uA==",
|
||||
"license": "MIT",
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/@types/earcut": {
|
||||
"version": "2.1.4",
|
||||
"resolved": "https://registry.npmjs.org/@types/earcut/-/earcut-2.1.4.tgz",
|
||||
"integrity": "sha512-qp3m9PPz4gULB9MhjGID7wpo3gJ4bTGXm7ltNDsmOvsPduTeHp8wSW9YckBj3mljeOh4F0m2z/0JKAALRKbmLQ==",
|
||||
"license": "MIT",
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/@types/estree": {
|
||||
"version": "1.0.8",
|
||||
"resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz",
|
||||
@@ -1363,6 +1229,12 @@
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/gradient-parser": {
|
||||
"version": "0.1.5",
|
||||
"resolved": "https://registry.npmjs.org/@types/gradient-parser/-/gradient-parser-0.1.5.tgz",
|
||||
"integrity": "sha512-r7K3NkJz3A95WkVVmjs0NcchhHstC2C/VIYNX4JC6tieviUNo774FFeOHjThr3Vw/WCeMP9kAT77MKbIRlO/4w==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/prop-types": {
|
||||
"version": "15.7.15",
|
||||
"resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.15.tgz",
|
||||
@@ -1504,23 +1376,6 @@
|
||||
"node": ">= 0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/call-bound": {
|
||||
"version": "1.0.4",
|
||||
"resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz",
|
||||
"integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"call-bind-apply-helpers": "^1.0.2",
|
||||
"get-intrinsic": "^1.3.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/caniuse-lite": {
|
||||
"version": "1.0.30001777",
|
||||
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001777.tgz",
|
||||
@@ -1608,13 +1463,6 @@
|
||||
"node": ">= 0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/earcut": {
|
||||
"version": "2.2.4",
|
||||
"resolved": "https://registry.npmjs.org/earcut/-/earcut-2.2.4.tgz",
|
||||
"integrity": "sha512-/pjZsA1b4RPHbeWZQn66SWS8nZZWLQQ23oE3Eam7aroEFGEvwKAsJfZ9ytiEMycfzXWpca4FA9QIOehf7PocBQ==",
|
||||
"license": "ISC",
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/electron-to-chromium": {
|
||||
"version": "1.5.307",
|
||||
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.307.tgz",
|
||||
@@ -1762,13 +1610,6 @@
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/eventemitter3": {
|
||||
"version": "4.0.7",
|
||||
"resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz",
|
||||
"integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==",
|
||||
"license": "MIT",
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/fdir": {
|
||||
"version": "6.5.0",
|
||||
"resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz",
|
||||
@@ -2082,19 +1923,6 @@
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/object-inspect": {
|
||||
"version": "1.13.4",
|
||||
"resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz",
|
||||
"integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/parse-svg-path": {
|
||||
"version": "0.1.2",
|
||||
"resolved": "https://registry.npmjs.org/parse-svg-path/-/parse-svg-path-0.1.2.tgz",
|
||||
@@ -2121,6 +1949,18 @@
|
||||
"url": "https://github.com/sponsors/jonschlinkert"
|
||||
}
|
||||
},
|
||||
"node_modules/pixi-filters": {
|
||||
"version": "6.1.5",
|
||||
"resolved": "https://registry.npmjs.org/pixi-filters/-/pixi-filters-6.1.5.tgz",
|
||||
"integrity": "sha512-Ewb/J+kxAbaNN+0/ATJbglAJG+skGJfh7BIDP3ILIDdD6wWk1p0pGa25pVf1T8hGBOQSUNVAmwwJBwkj+cyLLA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/gradient-parser": "^0.1.2"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"pixi.js": ">=8.0.0-0"
|
||||
}
|
||||
},
|
||||
"node_modules/pixi-viewport": {
|
||||
"version": "6.0.3",
|
||||
"resolved": "https://registry.npmjs.org/pixi-viewport/-/pixi-viewport-6.0.3.tgz",
|
||||
@@ -2209,22 +2049,6 @@
|
||||
"integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/qs": {
|
||||
"version": "6.15.0",
|
||||
"resolved": "https://registry.npmjs.org/qs/-/qs-6.15.0.tgz",
|
||||
"integrity": "sha512-mAZTtNCeetKMH+pSjrb76NAM8V9a05I9aBZOHztWy/UqcJdQYNsf59vrRKWnojAT9Y+GbIvoTBC++CPHqpDBhQ==",
|
||||
"license": "BSD-3-Clause",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"side-channel": "^1.1.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.6"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/react": {
|
||||
"version": "18.3.1",
|
||||
"resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz",
|
||||
@@ -2356,82 +2180,6 @@
|
||||
"semver": "bin/semver.js"
|
||||
}
|
||||
},
|
||||
"node_modules/side-channel": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz",
|
||||
"integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"es-errors": "^1.3.0",
|
||||
"object-inspect": "^1.13.3",
|
||||
"side-channel-list": "^1.0.0",
|
||||
"side-channel-map": "^1.0.1",
|
||||
"side-channel-weakmap": "^1.0.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/side-channel-list": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz",
|
||||
"integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"es-errors": "^1.3.0",
|
||||
"object-inspect": "^1.13.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/side-channel-map": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz",
|
||||
"integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"call-bound": "^1.0.2",
|
||||
"es-errors": "^1.3.0",
|
||||
"get-intrinsic": "^1.2.5",
|
||||
"object-inspect": "^1.13.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/side-channel-weakmap": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz",
|
||||
"integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"call-bound": "^1.0.2",
|
||||
"es-errors": "^1.3.0",
|
||||
"get-intrinsic": "^1.2.5",
|
||||
"object-inspect": "^1.13.3",
|
||||
"side-channel-map": "^1.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/socket.io-client": {
|
||||
"version": "4.8.3",
|
||||
"resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-4.8.3.tgz",
|
||||
@@ -2541,27 +2289,6 @@
|
||||
"browserslist": ">= 4.21.0"
|
||||
}
|
||||
},
|
||||
"node_modules/url": {
|
||||
"version": "0.11.4",
|
||||
"resolved": "https://registry.npmjs.org/url/-/url-0.11.4.tgz",
|
||||
"integrity": "sha512-oCwdVC7mTuWiPyjLUz/COz5TLk6wgp0RCsN+wHZ2Ekneac9w8uuV0njcbbie2ME+Vs+d6duwmYuR3HgQXs1fOg==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"punycode": "^1.4.1",
|
||||
"qs": "^6.12.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/url/node_modules/punycode": {
|
||||
"version": "1.4.1",
|
||||
"resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz",
|
||||
"integrity": "sha512-jmYNElW7yvO7TV33CjSmvSiE2yco3bV2czu/OzDKdMNVZQWfxCblURLhf+47syQRBntjfLdd/H0egrzIG+oaFQ==",
|
||||
"license": "MIT",
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/vite": {
|
||||
"version": "6.4.1",
|
||||
"resolved": "https://registry.npmjs.org/vite/-/vite-6.4.1.tgz",
|
||||
|
||||
@@ -9,8 +9,8 @@
|
||||
"preview": "vite preview"
|
||||
},
|
||||
"dependencies": {
|
||||
"@pixi/filter-drop-shadow": "^5.2.0",
|
||||
"axios": "^1.7.9",
|
||||
"pixi-filters": "^6.1.5",
|
||||
"pixi-viewport": "^6.0.3",
|
||||
"pixi.js": "^8.17.0",
|
||||
"react": "^18.3.1",
|
||||
|
||||
@@ -9,6 +9,8 @@ import { Container, Graphics, FederatedPointerEvent } from 'pixi.js';
|
||||
import type { Viewport } from 'pixi-viewport';
|
||||
import { SceneManager, SceneItem } from './SceneManager';
|
||||
import { TransformBox } from './TransformBox';
|
||||
import { ImageSprite } from './sprites/ImageSprite';
|
||||
import { Spring, PRESETS } from './spring';
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Constants
|
||||
@@ -20,6 +22,8 @@ const BAND_STROKE = 0x4a90d9;
|
||||
const BAND_STROKE_ALPHA = 0.6;
|
||||
const BAND_STROKE_WIDTH = 1;
|
||||
const RUBBER_BAND_THRESHOLD = 5; // px in screen space before rubber band activates
|
||||
const DRAG_THRESHOLD = 5; // px in screen space before object drag activates
|
||||
const DRAG_LIFT_SCALE = 1.03; // scale during drag
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// SelectionManager
|
||||
@@ -44,6 +48,11 @@ export class SelectionManager {
|
||||
private _rubberBanding = false;
|
||||
private _hitItemOnDown: SceneItem | null = null;
|
||||
|
||||
// Object drag state
|
||||
private _objectDragging = false;
|
||||
private _lastDragWorldX = 0;
|
||||
private _lastDragWorldY = 0;
|
||||
|
||||
constructor(viewport: Viewport, scene: SceneManager) {
|
||||
this._viewport = viewport;
|
||||
this._scene = scene;
|
||||
@@ -177,13 +186,46 @@ export class SelectionManager {
|
||||
private _onPointerMove(e: FederatedPointerEvent): void {
|
||||
if (!this._pointerDown) return;
|
||||
|
||||
// If we clicked on an object, don't start rubber banding
|
||||
if (this._hitItemOnDown) return;
|
||||
|
||||
const dx = e.global.x - this._downScreenX;
|
||||
const dy = e.global.y - this._downScreenY;
|
||||
const dist = Math.sqrt(dx * dx + dy * dy);
|
||||
|
||||
// Object drag: clicked on a selected item
|
||||
if (this._hitItemOnDown && this.selectedIds.has(this._hitItemOnDown.id)) {
|
||||
if (!this._objectDragging && dist >= DRAG_THRESHOLD) {
|
||||
this._objectDragging = true;
|
||||
this._lastDragWorldX = this._downWorldX;
|
||||
this._lastDragWorldY = this._downWorldY;
|
||||
|
||||
// Pause viewport drag so object drag works
|
||||
this._viewport.plugins.pause('drag');
|
||||
|
||||
// Lift shadow + spring scale on all selected image sprites
|
||||
this._applyLift();
|
||||
}
|
||||
|
||||
if (this._objectDragging) {
|
||||
const currentWorld = this._viewport.toWorld(e.global.x, e.global.y);
|
||||
const ddx = currentWorld.x - this._lastDragWorldX;
|
||||
const ddy = currentWorld.y - this._lastDragWorldY;
|
||||
this._lastDragWorldX = currentWorld.x;
|
||||
this._lastDragWorldY = currentWorld.y;
|
||||
|
||||
// Move all selected items by delta
|
||||
for (const item of this.getSelectedItems()) {
|
||||
item.displayObject.x += ddx;
|
||||
item.displayObject.y += ddy;
|
||||
item.data.x = item.displayObject.x;
|
||||
item.data.y = item.displayObject.y;
|
||||
}
|
||||
this.transformBox.update(this.getSelectedItems());
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Rubber band: clicked on empty space
|
||||
if (this._hitItemOnDown) return;
|
||||
|
||||
if (!this._rubberBanding && dist >= RUBBER_BAND_THRESHOLD) {
|
||||
this._rubberBanding = true;
|
||||
// Pause viewport drag so rubber band works
|
||||
@@ -200,7 +242,17 @@ export class SelectionManager {
|
||||
if (!this._pointerDown) return;
|
||||
this._pointerDown = false;
|
||||
|
||||
if (this._rubberBanding) {
|
||||
if (this._objectDragging) {
|
||||
// End object drag — drop shadow + spring scale back
|
||||
this._applyDrop();
|
||||
this._objectDragging = false;
|
||||
|
||||
// Resume viewport drag
|
||||
this._viewport.plugins.resume('drag');
|
||||
|
||||
// Notify change (positions updated)
|
||||
this._emitChange();
|
||||
} else if (this._rubberBanding) {
|
||||
// Finish rubber band selection
|
||||
const currentWorld = this._viewport.toWorld(e.global.x, e.global.y);
|
||||
this._finishRubberBand(this._downWorldX, this._downWorldY, currentWorld.x, currentWorld.y, e.shiftKey);
|
||||
@@ -220,6 +272,48 @@ export class SelectionManager {
|
||||
this._hitItemOnDown = null;
|
||||
}
|
||||
|
||||
// -- Drag Shadow Lift / Drop ----------------------------------------------
|
||||
|
||||
/** Lift shadows and spring-scale selected items up for drag. */
|
||||
private _applyLift(): void {
|
||||
const items = this.getSelectedItems();
|
||||
for (const item of items) {
|
||||
const dobj = item.displayObject;
|
||||
|
||||
// Lift shadow on ImageSprites
|
||||
if (dobj instanceof ImageSprite) {
|
||||
dobj.liftShadow();
|
||||
}
|
||||
|
||||
// Spring scale 1.0 → 1.03
|
||||
const spring = new Spring(1.0, DRAG_LIFT_SCALE, PRESETS.snappy);
|
||||
spring.onUpdate = (v) => {
|
||||
dobj.scale.set(v, v);
|
||||
};
|
||||
this._scene.springs.add(spring);
|
||||
}
|
||||
}
|
||||
|
||||
/** Drop shadows and spring-scale selected items back to rest. */
|
||||
private _applyDrop(): void {
|
||||
const items = this.getSelectedItems();
|
||||
for (const item of items) {
|
||||
const dobj = item.displayObject;
|
||||
|
||||
// Drop shadow on ImageSprites
|
||||
if (dobj instanceof ImageSprite) {
|
||||
dobj.dropShadow();
|
||||
}
|
||||
|
||||
// Spring scale 1.03 → 1.0
|
||||
const spring = new Spring(DRAG_LIFT_SCALE, 1.0, PRESETS.snappy);
|
||||
spring.onUpdate = (v) => {
|
||||
dobj.scale.set(v, v);
|
||||
};
|
||||
this._scene.springs.add(spring);
|
||||
}
|
||||
}
|
||||
|
||||
// -- Rubber Band ----------------------------------------------------------
|
||||
|
||||
private _drawRubberBand(x1: number, y1: number, x2: number, y2: number): void {
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { Sprite, Texture, Graphics } from "pixi.js";
|
||||
import { DropShadowFilter } from "pixi-filters";
|
||||
import { TextureManager, LODTier } from "../TextureManager";
|
||||
|
||||
/**
|
||||
@@ -6,8 +7,14 @@ import { TextureManager, LODTier } from "../TextureManager";
|
||||
* Shows a placeholder shimmer rect until the first texture tier loads,
|
||||
* then swaps textures as zoom level changes.
|
||||
*/
|
||||
// Shadow defaults (resting state)
|
||||
const SHADOW_REST = { offsetX: 3, offsetY: 3, blur: 4, alpha: 0.25 };
|
||||
// Shadow lifted state (during drag)
|
||||
const SHADOW_LIFT = { offsetX: 6, offsetY: 8, blur: 8, alpha: 0.35 };
|
||||
|
||||
export class ImageSprite extends Sprite {
|
||||
readonly assetKey: string;
|
||||
readonly shadow: DropShadowFilter;
|
||||
|
||||
private textures: TextureManager;
|
||||
private currentTier: LODTier | null = null;
|
||||
@@ -32,6 +39,15 @@ export class ImageSprite extends Sprite {
|
||||
this.width = w;
|
||||
this.height = h;
|
||||
|
||||
// Drop shadow for photos-on-a-desk feel
|
||||
this.shadow = new DropShadowFilter({
|
||||
offset: { x: SHADOW_REST.offsetX, y: SHADOW_REST.offsetY },
|
||||
blur: SHADOW_REST.blur,
|
||||
alpha: SHADOW_REST.alpha,
|
||||
color: 0x000000,
|
||||
});
|
||||
this.filters = [this.shadow];
|
||||
|
||||
// Create placeholder: dark rect shown until first texture loads
|
||||
const placeholder = new Graphics();
|
||||
placeholder.rect(0, 0, w, h).fill(0x2a2a2a);
|
||||
@@ -42,6 +58,20 @@ export class ImageSprite extends Sprite {
|
||||
this.loadTier("thumb");
|
||||
}
|
||||
|
||||
/** Expand shadow for drag-lift effect. */
|
||||
liftShadow(): void {
|
||||
this.shadow.offset = { x: SHADOW_LIFT.offsetX, y: SHADOW_LIFT.offsetY };
|
||||
this.shadow.blur = SHADOW_LIFT.blur;
|
||||
this.shadow.alpha = SHADOW_LIFT.alpha;
|
||||
}
|
||||
|
||||
/** Restore shadow to resting state. */
|
||||
dropShadow(): void {
|
||||
this.shadow.offset = { x: SHADOW_REST.offsetX, y: SHADOW_REST.offsetY };
|
||||
this.shadow.blur = SHADOW_REST.blur;
|
||||
this.shadow.alpha = SHADOW_REST.alpha;
|
||||
}
|
||||
|
||||
/**
|
||||
* Load a specific LOD tier texture for this sprite.
|
||||
* Skips if already at the requested tier or currently loading.
|
||||
|
||||
Reference in New Issue
Block a user