diff --git a/README.md b/README.md index b27c400b..98f2e99d 100644 --- a/README.md +++ b/README.md @@ -96,9 +96,9 @@ SVGs of up to size 480 x 360 will fit into the view window of the paint editor, `imageFormat`: 'svg', 'png', or 'jpg'. Other formats are currently not supported. -`rotationCenterX`: x coordinate relative to the top left corner of the sprite of the point that should be centered. +`rotationCenterX`: x coordinate relative to the top left corner of the sprite of the point that should be centered. If left undefined, image will be horizontally centered. -`rotationCenterY`: y coordinate relative to the top left corner of the sprite of the point that should be centered. +`rotationCenterY`: y coordinate relative to the top left corner of the sprite of the point that should be centered. If left undefined, image will be vertcally centered. `rtl`: True if the paint editor should be laid out right to left (meant for right to left languages) diff --git a/src/containers/paper-canvas.jsx b/src/containers/paper-canvas.jsx index 2b2ce63a..256f864b 100644 --- a/src/containers/paper-canvas.jsx +++ b/src/containers/paper-canvas.jsx @@ -148,6 +148,13 @@ class PaperCanvas extends React.Component { if (!this.queuedImageToLoad) return; this.queuedImageToLoad = null; + if (typeof rotationCenterX === 'undefined') { + rotationCenterX = imgElement.width / 2; + } + if (typeof rotationCenterY === 'undefined') { + rotationCenterY = imgElement.height / 2; + } + getRaster().drawImage( imgElement, (ART_BOARD_WIDTH / 2) - rotationCenterX, diff --git a/src/playground/playground.css b/src/playground/playground.css index f6144dfe..0b8e3f3d 100644 --- a/src/playground/playground.css +++ b/src/playground/playground.css @@ -4,7 +4,7 @@ body { margin: 0px; } -body, html { +body, html, .wrapper { height: 100% } @@ -12,4 +12,12 @@ body, html { height: 90%; width: 90%; margin: auto; -} \ No newline at end of file +} + +#fileInput { + display: none; +} + +.playgroundButton { + margin: 4px; +} diff --git a/src/playground/playground.jsx b/src/playground/playground.jsx index 8ccc8684..963ed980 100644 --- a/src/playground/playground.jsx +++ b/src/playground/playground.jsx @@ -29,50 +29,152 @@ class Playground extends React.Component { constructor (props) { super(props); bindAll(this, [ + 'downloadImage', 'handleUpdateName', - 'handleUpdateImage' + 'handleUpdateImage', + 'onUploadImage' ]); // Append ?dir=rtl to URL to get RTL layout const match = location.search.match(/dir=([^&]+)/); const rtl = match && match[1] == 'rtl'; + this.id = 0; this.state = { name: 'meow', rotationCenterX: 20, rotationCenterY: 400, imageFormat: 'svg', // 'svg', 'png', or 'jpg' image: svgString, // svg string or data URI - rtl: rtl + imageId: this.id, // If this changes, the paint editor will reload + rtl: rtl, }; + this.reusableCanvas = document.createElement('canvas'); } handleUpdateName (name) { this.setState({name}); } handleUpdateImage (isVector, image, rotationCenterX, rotationCenterY) { - console.log(image); + this.setState({ + imageFormat: isVector ? 'svg' : 'png' + }); + if (!isVector) { + console.log(`Image width: ${image.width} Image height: ${image.height}`); + } console.log(`rotationCenterX: ${rotationCenterX} rotationCenterY: ${rotationCenterY}`); if (isVector) { this.setState({image, rotationCenterX, rotationCenterY}); } else { // is Bitmap // image parameter has type ImageData // paint editor takes dataURI as input - const canvas = document.createElement('canvas'); - const context = canvas.getContext('2d'); + this.reusableCanvas.width = image.width; + this.reusableCanvas.height = image.height; + const context = this.reusableCanvas.getContext('2d'); context.putImageData(image, 0, 0); this.setState({ - image: canvas.toDataURL('image/png'), + image: this.reusableCanvas.toDataURL('image/png'), rotationCenterX: rotationCenterX, rotationCenterY: rotationCenterY }); } } + downloadImage () { + const downloadLink = document.createElement('a'); + document.body.appendChild(downloadLink); + + const format = this.state.imageFormat; + let data = this.state.image; + if (format === 'png' || format === 'jpg') { + data = this.b64toByteArray(data); + } else { + data = [data]; + } + const blob = new Blob(data, {type: format}); + const filename = `${this.state.name}.${format}`; + if ('download' in HTMLAnchorElement.prototype) { + const url = window.URL.createObjectURL(blob); + downloadLink.href = url; + downloadLink.download = filename; + downloadLink.type = blob.type; + downloadLink.click(); + window.URL.revokeObjectURL(url); + } else { + // iOS Safari, open a new page and set href to data-uri + let popup = window.open('', '_blank'); + const reader = new FileReader(); + reader.onloadend = function () { + popup.location.href = reader.result; + popup = null; + }; + reader.readAsDataURL(blob); + } + document.body.removeChild(downloadLink); + } + b64toByteArray (b64Data, sliceSize=512) { + // Remove header + b64Data = b64Data.substring(b64Data.indexOf('base64,') + 7); + + const byteCharacters = atob(b64Data); + const byteArrays = []; + + for (let offset = 0; offset < byteCharacters.length; offset += sliceSize) { + const slice = byteCharacters.slice(offset, offset + sliceSize); + + const byteNumbers = new Array(slice.length); + for (let i = 0; i < slice.length; i++) { + byteNumbers[i] = slice.charCodeAt(i); + } + + const byteArray = new Uint8Array(byteNumbers); + byteArrays.push(byteArray); + } + + return byteArrays; + } + uploadImage() { + document.getElementById(styles.fileInput).click(); + } + onUploadImage(event) { + var file = event.target.files[0]; + var type = file.type === 'image/svg+xml' ? 'svg' : + file.type === 'image/png' ? 'png' : + file.type === 'image/jpg' ? 'jpg' : + file.type === 'image/jpeg' ? 'jpg' : + null; + + var reader = new FileReader(); + if (type === 'svg') { + reader.readAsText(file,'UTF-8'); + } else if (type === 'png' || type === 'jpg'){ + reader.readAsDataURL(file); + } else { + alert("Couldn't read file type: " + file.type); + } + + const that = this; + reader.onload = readerEvent => { + var content = readerEvent.target.result; // this is the content! + + that.setState({ + image: content, + name: file.name.split('.').slice(0, -1).join('.'), + imageId: ++that.id, + imageFormat: type, + rotationCenterX: undefined, + rotationCenterY: undefined, + }); + } + } render () { return ( - +
+ + + + +
); }