diff --git a/package.json b/package.json
index 7bbd6c3..90663dc 100644
--- a/package.json
+++ b/package.json
@@ -12,6 +12,7 @@
"sass": "^1.34.0"
},
"dependencies": {
- "jquery": "^3.6.0"
+ "jquery": "^3.6.0",
+ "jszip": "^3.10.1"
}
}
diff --git a/src/cutter/PreviewPanel.js b/src/cutter/PreviewPanel.js
index 3e4cad6..106f56f 100644
--- a/src/cutter/PreviewPanel.js
+++ b/src/cutter/PreviewPanel.js
@@ -1,6 +1,6 @@
import $ from 'jquery';
+import JSZip from 'jszip';
-import Rect from '../spritecow/Rect';
import InlineEdit from '../spritecow/InlineEdit';
class PreviewPanel {
@@ -16,8 +16,8 @@ class PreviewPanel {
this.$exportButton = this.$settings.find('#exportButton');
this.$spriteCanvas = spriteCanvas.canvas;
- this.fileName = 'sprite.png';
- this.rect = new Rect(0, 0, 0, 0);
+ this.fileName = 'sprite';
+ this.selectedSprites = [];
this._addEditEvents();
this.$exportButton.on('click', this.handleExport.bind(this));
@@ -48,17 +48,18 @@ class PreviewPanel {
$('
Settings
').appendTo(container);
$('Name:
').appendTo(container);
+ $('0 sprite(s) selected!
').appendTo(container);
$('').appendTo(container);
return container;
}
update() {
- var rect = this.rect;
+ var rect = this.selectedSprites[this.selectedSprites.length - 1].rect;
const previewCanvas = this.$previewCanvas;
- const hiddenCanvas = this.$hiddenExportingCanvas;
this.$settings.find('#fileName').text(this.fileName);
+ this.$settings.find('#selectedSpritesCount').text(this.selectedSprites.length);
this.$properties.find('#topX').val(rect.x);
this.$properties.find('#topY').val(rect.y);
this.$properties.find('#width').val(rect.width);
@@ -71,9 +72,35 @@ class PreviewPanel {
this.$spriteCanvas, rect.x, rect.y, rect.width, rect.height,
0, 0, previewCanvas.width, previewCanvas.height
);
+ };
+
+ handleExport() {
+ if(this.selectedSprites.length > 1) {
+ this.createZipExport().then(base64 => {
+ const dataUrl = "data:application/zip;base64," + base64;
+ this.downloadExport(dataUrl, '.zip');
+ });
+ } else {
+ const image = this.createImageData(this.selectedSprites[0]);
+ this.downloadExport(image, '.png');
+ }
+ }
+
+ downloadExport(dataUrl, ext) {
+ var link = document.createElement('a');
+ link.download = this.fileName + ext;
+ link.href = dataUrl;
+
+ document.body.appendChild(link);
+ link.click();
+ document.body.removeChild(link);
+ }
- // we need a hidden canvas that will always match the current selected sprite's height/width so when exporting it is the correct size
+ createImageData(sprite) {
+ const rect = sprite.rect;
+ const hiddenCanvas = this.$hiddenExportingCanvas;
const hiddenCanvasContext = hiddenCanvas.getContext('2d');
+
hiddenCanvasContext.clearRect(0, 0, hiddenCanvas.width, hiddenCanvas.height);
hiddenCanvas.width = rect.width;
hiddenCanvas.height = rect.height;
@@ -81,17 +108,19 @@ class PreviewPanel {
this.$spriteCanvas, rect.x, rect.y, rect.width, rect.height,
0, 0, hiddenCanvas.width, hiddenCanvas.height
);
- };
- handleExport() {
- const image = this.$hiddenExportingCanvas.toDataURL("image/png");
- var link = document.createElement('a');
- link.download = this.fileName;
- link.href = image;
+ return hiddenCanvas.toDataURL("image/png");
+ }
- document.body.appendChild(link);
- link.click();
- document.body.removeChild(link);
+ createZipExport() {
+ var zip = new JSZip();
+
+ this.selectedSprites.forEach((sprite, i) => {
+ const base64Image = this.createImageData(sprite).split(';base64,')[1];
+ zip.file(`${i}.png`, base64Image, {base64: true});
+ });
+
+ return zip.generateAsync({type:"base64"});
}
_addEditEvents() {
diff --git a/src/spritecow/SpriteCanvasView.js b/src/spritecow/SpriteCanvasView.js
index 1d218eb..4a589ce 100644
--- a/src/spritecow/SpriteCanvasView.js
+++ b/src/spritecow/SpriteCanvasView.js
@@ -263,10 +263,6 @@ class SpriteCanvasView {
var SpriteCanvasViewProto = SpriteCanvasView.prototype = new MicroEvent;
-SpriteCanvasViewProto._setCurrentRect = function(rect) {
- this.trigger('rectChange', rect);
-};
-
SpriteCanvasViewProto._handleSelectedSprite = function(clickedRect, spriteRect) {
if(isKeyDown(SHIFT_KEY)) {
const alreadySelectedSpriteIndex = this._selectedSprites.findIndex(sprite => JSON.stringify(sprite.rect) == JSON.stringify(spriteRect));
@@ -281,7 +277,7 @@ SpriteCanvasViewProto._handleSelectedSprite = function(clickedRect, spriteRect)
this._selectedSprites = [this._selectSprite(clickedRect, spriteRect)];
}
- this._setCurrentRect(spriteRect);
+ this.trigger('selectedSpritesChange', this._selectedSprites);
}
SpriteCanvasViewProto._selectSprite = function(clickedRect, spriteRect) {
diff --git a/src/spritecow/base.js b/src/spritecow/base.js
index a3265ac..79de86e 100644
--- a/src/spritecow/base.js
+++ b/src/spritecow/base.js
@@ -52,16 +52,22 @@ import PreviewPanel from '../cutter/PreviewPanel';
pageLayout.toAppView();
});
- spriteCanvasView.bind('rectChange', function(rect) {
- previewPanel.rect = rect;
+ spriteCanvasView.bind('selectedSpritesChange', function(selectedSprites) {
+ if(selectedSprites.length === 0) {
+ return;
+ }
+
+ previewPanel.selectedSprites = selectedSprites;
previewPanel.update();
- if (rect.width === spriteCanvas.canvas.width && rect.height === spriteCanvas.canvas.height) {
- // if the rect is the same size as the whole canvas,
- // it's probably because the background is set wrong
- // let's be kind...
- toolbarTop.feedback( 'Incorrect background colour set?', true );
- }
+ selectedSprites.forEach(({rect}) => {
+ if (rect.width === spriteCanvas.canvas.width && rect.height === spriteCanvas.canvas.height) {
+ // if the rect is the same size as the whole canvas,
+ // it's probably because the background is set wrong
+ // let's be kind...
+ toolbarTop.feedback( 'Incorrect background colour set?', true );
+ }
+ });
});
spriteCanvasView.bind('bgColorHover', function(color) {
diff --git a/yarn.lock b/yarn.lock
index 6d1b75c..22566c5 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -2802,6 +2802,11 @@ ieee754@^1.1.4:
resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352"
integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==
+immediate@~3.0.5:
+ version "3.0.6"
+ resolved "https://registry.yarnpkg.com/immediate/-/immediate-3.0.6.tgz#9db1dbd0faf8de6fbe0f5dd5e56bb606280de69b"
+ integrity sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==
+
import-fresh@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-2.0.0.tgz#d81355c15612d386c61f9ddd3922d4304822a546"
@@ -3227,6 +3232,16 @@ jsprim@^1.2.2:
json-schema "0.2.3"
verror "1.10.0"
+jszip@^3.10.1:
+ version "3.10.1"
+ resolved "https://registry.yarnpkg.com/jszip/-/jszip-3.10.1.tgz#34aee70eb18ea1faec2f589208a157d1feb091c2"
+ integrity sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g==
+ dependencies:
+ lie "~3.3.0"
+ pako "~1.0.2"
+ readable-stream "~2.3.6"
+ setimmediate "^1.0.5"
+
kind-of@^3.0.2, kind-of@^3.0.3, kind-of@^3.2.0:
version "3.2.2"
resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-3.2.2.tgz#31ea21a734bab9bbb0f32466d893aea51e4a3c64"
@@ -3259,6 +3274,13 @@ levn@~0.3.0:
prelude-ls "~1.1.2"
type-check "~0.3.2"
+lie@~3.3.0:
+ version "3.3.0"
+ resolved "https://registry.yarnpkg.com/lie/-/lie-3.3.0.tgz#dcf82dee545f46074daf200c7c1c5a08e0f40f6a"
+ integrity sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==
+ dependencies:
+ immediate "~3.0.5"
+
lodash.clone@^4.5.0:
version "4.5.0"
resolved "https://registry.yarnpkg.com/lodash.clone/-/lodash.clone-4.5.0.tgz#195870450f5a13192478df4bc3d23d2dea1907b6"
@@ -3686,7 +3708,7 @@ pako@^0.2.5:
resolved "https://registry.yarnpkg.com/pako/-/pako-0.2.9.tgz#f3f7522f4ef782348da8161bad9ecfd51bf83a75"
integrity sha1-8/dSL073gjSNqBYbrZ7P1Rv4OnU=
-pako@~1.0.5:
+pako@~1.0.2, pako@~1.0.5:
version "1.0.11"
resolved "https://registry.yarnpkg.com/pako/-/pako-1.0.11.tgz#6c9599d340d54dfd3946380252a35705a6b992bf"
integrity sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==
@@ -4658,7 +4680,7 @@ set-value@^2.0.0, set-value@^2.0.1:
is-plain-object "^2.0.3"
split-string "^3.0.1"
-setimmediate@^1.0.4:
+setimmediate@^1.0.4, setimmediate@^1.0.5:
version "1.0.5"
resolved "https://registry.yarnpkg.com/setimmediate/-/setimmediate-1.0.5.tgz#290cbb232e306942d7d7ea9b83732ab7856f8285"
integrity sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU=