first of all, Im new to this webpage and Im really sorry for any mistakes.
So, I needed to animate a png image of a Calligraphy text just like a handwriting effect.
Recently I've watched a youtube video https://www.youtube.com/watch?v=wab7lQKHXL4, its about making a handwriting animation with strokes clipped to texts.
So I was wondering, if I could clip the strokes to a png image and animate it just like the handwriting animation.
I tried to import the png image inside inkspace and drawn path on top of it, gave suitable stroke-width saved it as a svg file, and tried to animate it with stroke-dasharray, but its not working ,its not even showing up.
although I can do the same with written texts just like in the video.
So is there any way ,I can achieve the same goal using a png image.
thanks in advance, and sorry if I don't make any sense
The video you are linking is astonishing, if not crooked. The commentary points out it is done with a "custom" version of Inkscape, and I have to say there are some UI elements I have never seen. (Admittedly, I am on an older version from 2021, but the video is from 2017, when Inkscape oficially wasn't even on verson 1.0.) Maybe the specialists on https://graphicdesign.stackexchange.com/ can tell you more about this "custom" version?
The core action is selecting from the menu bar Object -> Set Clip. If this is the same as selecting Object -> Clip -> Set in my version (1.02), this cannot work. This action sets a clip-path. Clip paths only clip to their fill area and have no stroke properties.
What you have to do instead is
set the fill to none and the stroke color to white (#ffffff)
select a Object -> Mask -> Set
Also, even after watching for multiple times, I am not sure on which element the onload attribute is set. Setting it on the path element in the mask certainly won't work, there is no load event happening there.
The logical choice would be to wait for the image loading.
The result should be something like this:
document.querySelector('image#letter').addEventListener('load', function () {
setTimeout(() => document.querySelector('#stroke .dashed').style.strokeDashoffset = 0, 200)
})
.dashed {
fill: none;
stroke: white;
stroke-width: 8px;
stroke-dasharray: 100;
stroke-dashoffset: 100;
transition: stroke-dashoffset 1s linear;
}
image#letter {
mask: url(#stroke);
}
<svg viewBox="0 0 100 100" width="100" height="100">
<mask id="stroke" maskUnits="userSpaceOnUse">
<path class="dashed" d="M 61.739,30.223 C 61.739,30.223 55.369,26.515 51.839,25.606 48.819,24.825 45.469,24.022 42.489,24.964 40.379,25.63 38.329,27.161 37.349,29.137 35.899,32.044 35.099,36.236 36.989,38.878 36.989,38.878 46.459,41.753 50.839,42.679 54.399,43.431 57.729,48.053 56.429,52.972 55.599,56.117 51.469,57.478 48.379,58.502 45.569,59.433 42.389,59.495 39.509,58.837 36.089,58.056 30.199,53.913 30.199,53.913" />
</mask>
<image id="letter" width="100" height="100"
xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGQAAABkCAYAAABw4pVUAAAACXBIWXMAAA7DAAAOwwHHb6hkAAAA
GXRFWHRTb2Z0d2FyZQB3d3cuaW5rc2NhcGUub3Jnm+48GgAAAwxJREFUeJzt2zuIHWUYBuAnm2hi
NAnBoCJE8FIERTQRBRErgyIGSysRL6CtYqOgTUhh4xJILAUNYqFdai0sRBSUxAvEJsRLlIAXMOom
XlaL/wjx7Jxkz+ycmdlz3gcGln+Z4Z39ds4/881/iIiIiIiIiIiIiIiIiIiIiIgY19quA9SwFlux
afDz2W7jNGtN1wEuYB0ewD24HTcphTjXIk7iOL7ER4Pti8HvogHr8QK+wz81t1N4Dfe1G3363IrP
1S/E8PZeu/FXZl3XAYbsxLvKHHE+v+APbMBlkw41q7YqHzNV/+XH8Lwyj2wc2u9i7MDDOIivreIr
pE8OWlqIP/GM8a7kNUrhXsFpKUgtm3DG0oI8ssLjbsGeFR5jJj1klU/GTZnrOsDAbRVjb7eeogf6
UpCrKsaOt56iB/pSkKpb175ka1VfTvqnirEbW0/RA30pyImKscdxUcs5YuBO1Q+Eb+LSDnPNrDmj
+1ff4DnlaTxatEdpl5+vUXgSb+Ap7JKPtIl70Xid3DN4Hy/hfqVtHw17FL+p12r/AQdwfduhp93V
SnNwQb3CnMXLSns+GrQZj+EwfjZ+YT5U3QXorb6/Uz/XHG7GHcqEvkt5oXWhif1j3K1cbTFhG3Ev
XsWvRl8p+7sKOMuuwCHVBVlQ5qXowD7VRXm6y1CzbA6fWlqQw12GWq6+NBebtIjXK8ZvaDtIHdNY
EDhSMbat9RQ1TGtBfq8Y+6v1FDVMa0Gq7qhOtZ6ihmktyO6KsRNth1jNHlRWtjfhWmWB3PBd1pMN
HX8mzONvvKW0Q+q6UpnQq9r0q6qn1bV5//8DHsWzuGaZ+2/AE0Z/fWFfw3knpi/NxXllDW+Vb/EB
PsOPyjuPRWX56XbcoswZm0fsfwR3qb7zihGGr5Cmtk9weYvnMTV24x3lWaGJQixgLy5p8ySm0Tbl
hdQhfGX8QhxT3ssvd+7pnb7MIaNsV1Yw7sB1yjzx35c+Tw+275X55aiyZCgiIiIiIiIiIiIiIiIi
IiIiIiIiIiIiIiIiIiIiIiIm51/fJjIk1BlBegAAAABJRU5ErkJggg==
"
</svg>
Once you have a d-path, you can offload the work to a Native Web Component <draw-path>
disclaimer: The last drawn path is from Jake Archibald his 2013 blog: Animated Line Drawing SVG
window.customElements.define("draw-path", class extends HTMLElement {
constructor() {
let template = (id) => document.getElementById(id).content.cloneNode(true);
super() // super sets and returns this scope
.attachShadow({mode: "open"}) // sets and returns this.shadowRoot
.append(template(this.nodeName));
this.line = this.shadowRoot.querySelector("#line");
this.line.setAttribute("d", this.getAttribute("d") || "m10 60c30-70 55-70 75 0s55 70 85 0");
this.line.setAttribute("stroke", this.getAttribute("stroke") || "black");
this.line.setAttribute("stroke-width", this.getAttribute("stroke-width") || "2");
this.pen = this.shadowRoot.querySelector("#pen");
this.onmouseover = (evt) => this.draw();
}
connectedCallback() {
this.shadowRoot.querySelector("svg").setAttribute("viewBox",this.getAttribute("viewBox")||"0 0 180 150");
this.draw();
}
showpen(state = true, scale) {
this.pen.style.display = state ? 'initial' : 'none';
}
draw() {
clearInterval(this.drawing);
this.showpen();
this.dashoffset = 1;
this.pathlength = this.line.getTotalLength();
this.drawing = setInterval(() => this.update(), 50);
}
update() {
this.dashoffset -= this.getAttribute("speed") || 0.02;
let {x,y} = this.line.getPointAtLength(this.pathlength - this.dashoffset * this.pathlength);
this.pen.setAttribute("transform", `translate(${x-2} ${y-2})`);
this.line.style.strokeDashoffset = this.dashoffset;
if (this.dashoffset <= 0) this.end();
}
end() {
clearInterval(this.drawing);
this.showpen(false);
//console.log("end",this.line);
clearTimeout(this.timeout);
this.timeout = setTimeout(()=>this.draw(),2000);
}
});
<draw-path viewBox="20 20 60 60" d='M 61.739,30.223 C 61.739,30.223 55.369,26.515 51.839,25.606 48.819,24.825 45.469,24.022 42.489,24.964 40.379,25.63 38.329,27.161 37.349,29.137 35.899,32.044 35.099,36.236 36.989,38.878 36.989,38.878 46.459,41.753 50.839,42.679 54.399,43.431 57.729,48.053 56.429,52.972 55.599,56.117 51.469,57.478 48.379,58.502 45.569,59.433 42.389,59.495 39.509,58.837 36.089,58.056 30.199,53.913 30.199,53.913'></draw-path>
<draw-path stroke='red' stroke-width='8' speed=".01"></draw-path>
<draw-path stroke="deeppink" viewBox="0 0 400 370" d="M11.6 269s-19.7-42.4 6.06-68.2 48.5-6.06 59.1 12.1l-3.03 28.8 209-227s45.5-21.2 60.6 1.52c15.2 22.7-3.03 47-3.03 47l-225 229s33.1-12 48.5 7.58c50 63.6-50 97-62.1 37.9"></draw-path>
<template id="DRAW-PATH">
<style>
:host { display: inline-block }
svg { width: 180px; height: 130px; background: beige }
</style>
<svg xmlns="http://www.w3.org/2000/svg">
<path id='line' pathlength='1' stroke-dasharray='1' stroke-dashoffse='1' fill='transparent'/>
<path id='pen' stroke='black' stroke-width='2' fill='gold' d='m12 19l7-7l3 3l-7 7l-3-3zm6-6l-2-8l-14-3l4 15l7 1l5-5zm-16-11l8 8m-1 1a2 2 0 104 0a2 2 0 10-4 0'/>
</svg>
</template>
Note:
Be aware M or m (moves) in paths create a new stroke, drawn at the same time, not sequentially.
So stroke-dash* settings are applied concurrent.
That is why in all blogs you only see single stroke simple paths or polylines used.
I am working with SVG from some time now. But still I can't understand the viewbox after watching many videos and reading many articles.
I am trying to generate SVG dynamically:
import SvgIcon from '#material-ui/core/SvgIcon';
<SvgIcon className="svg" width="24" height="24" viewBox="0 0 24 24">
<g>
<path
d={scrapbookElement.icon.path}
/>
</g>
</SvgIcon>
Here is my JSON file which provides me scrapbookElements:
[
{
"id": "image",
"text": "Image",
"icon": {
"path": "M9.5999776,12.699997L11.899997,15.099998 13.299991,14.300003 19.000005,20 22.899999,18 28.299995,20.699997 28.299995,25.699997 3.5999767,25.699997 3.5999767,18.599998z M20.500005,11.699997L21.199987,11.699997 21.000005,12.800003 20.699987,12.800003z M23.09998,10.699997L23.799993,11.599998 23.59998,11.800003 22.699987,11.099998z M18.699987,10.699997L19.199987,11.199997 18.299993,11.900002 18.000005,11.599998z M23.59998,8.5999985L24.699987,8.8000031 24.699987,9 23.59998,9.1999969z M18.09998,8.5999985L18.09998,9.3000031 17.000005,9 17.000005,8.8000031z M20.799993,7C21.899999,7 22.699987,7.9000015 22.699987,8.9000015 22.699987,10 21.799993,10.800003 20.799993,10.800003 19.699987,10.800003 18.899999,9.9000015 18.899999,8.9000015 18.899999,7.8000031 19.799993,7 20.799993,7z M23.500005,6.0999985L23.699987,6.3000031 23.000005,7.1999969 22.500005,6.6999969z M18.199987,6.0999985L19.09998,6.8000031 18.59998,7.3000031 18.000005,6.3000031z M20.699987,5L21.000005,5 21.199987,6.0999985 20.500005,6.0999985z M2.1999823,2.4000015L2.1999823,26.800003 29.400001,26.800003 29.400001,2.4000015z M0,0L31.900001,0 31.900001,32 0,32z"
}
},
{
"id": "text",
"text": "Text",
"icon": {
"path": "M29.15,0L30.186011,0.012001038 30.610999,0.43099976 31.096015,3.868 31.209998,7.8079987 31.101996,8.3949966 30.610999,8.6230011 29.875007,8.5510025 29.437018,7.9520035 28.312018,5.298996 26.993995,3.7130013 25.82801,3.072998 24.294013,2.6170006 22.390996,2.3429985 20.12,2.2509995 18.580998,2.3590012 17.940007,2.6829987 17.868993,3.0900002 17.868993,26.970001 18.048009,28.272003 18.58701,29.113998 19.659001,29.697998 21.437016,30.227997 22.149999,30.389 22.467015,30.490997 22.707005,31.401001 22.179997,32 21.910008,31.994003 21.198003,31.928001 15.424992,31.640999 10.755008,31.879997 9.4850186,31.879997 9.1740131,31.305 9.2879972,30.665001 10.011996,30.275002 11.737002,29.695 12.791018,29.005997 13.401005,27.909996 13.605015,26.298996 13.605015,3.0900002 13.390018,2.5390015 12.743014,2.2750015 12.342013,2.2579994 11.329989,2.2509995 7.3779925,2.6469994 4.6229872,3.8320007 3.0840158,5.4970016 1.7250066,8.1439972 1.2399907,8.7959976 0.598999,8.7900009 0.041992187,8.4729996 0,7.9039993 0.62899778,4.7839966 0.83898971,0.59899902 1.2040105,0.20999908 2.2999882,0.14400101 7.7489947,0.39500046 13.748997,0.47900009 19.16602,0.44900131 23.539008,0.35900116 26.867011,0.20999908z"
}
}
]
Here is the output:
I can handle that with overflow: visible in css. But I dont want to do it because then i will get problems with height and width, which will lead me to incorrect centering using flexbox.
Here is codesandbox that reproduces the issue:
Click here for code sandbox
Basically if you want to fit SVG into a container you need to set the viewBox attribute to exact values of its content. In this case, from the points in the d attributes you can see that it's 0 0 32 32 (as in: x, y, width, height).
Then you can resize the SVG using the width and height attributes to stretch/shrink to the container you want it to fit into (taking into account aspect ratio, of course).
If you don't know the right value of viewBox for a given SVG, and you don't have strokes - like the examples above, you can try using dev tools in the browser and call .getBBox() on the <svg> element, which will give you the x, y, width, and height to place in the viewBox.
In order to get the size of a path I've transformed yours icons in true svg
console.log(test.getBBox());
console.log(test1.getBBox())
<svg className="svg" width="24" height="24" viewBox="0 0 32 32" >
<g>
<path id="test"
d="M9.5999776,12.699997L11.899997,15.099998 13.299991,14.300003 19.000005,20 22.899999,18 28.299995,20.699997 28.299995,25.699997 3.5999767,25.699997 3.5999767,18.599998z M20.500005,11.699997L21.199987,11.699997 21.000005,12.800003 20.699987,12.800003z M23.09998,10.699997L23.799993,11.599998 23.59998,11.800003 22.699987,11.099998z M18.699987,10.699997L19.199987,11.199997 18.299993,11.900002 18.000005,11.599998z M23.59998,8.5999985L24.699987,8.8000031 24.699987,9 23.59998,9.1999969z M18.09998,8.5999985L18.09998,9.3000031 17.000005,9 17.000005,8.8000031z M20.799993,7C21.899999,7 22.699987,7.9000015 22.699987,8.9000015 22.699987,10 21.799993,10.800003 20.799993,10.800003 19.699987,10.800003 18.899999,9.9000015 18.899999,8.9000015 18.899999,7.8000031 19.799993,7 20.799993,7z M23.500005,6.0999985L23.699987,6.3000031 23.000005,7.1999969 22.500005,6.6999969z M18.199987,6.0999985L19.09998,6.8000031 18.59998,7.3000031 18.000005,6.3000031z M20.699987,5L21.000005,5 21.199987,6.0999985 20.500005,6.0999985z M2.1999823,2.4000015L2.1999823,26.800003 29.400001,26.800003 29.400001,2.4000015z M0,0L31.900001,0 31.900001,32 0,32z"
/>
</g>
</svg>
<hr>
<svg className="svg" width="24" height="24" viewBox="0 0 32 32" >
<g>
<path id="test1"
d="M29.15,0L30.186011,0.012001038 30.610999,0.43099976 31.096015,3.868 31.209998,7.8079987 31.101996,8.3949966 30.610999,8.6230011 29.875007,8.5510025 29.437018,7.9520035 28.312018,5.298996 26.993995,3.7130013 25.82801,3.072998 24.294013,2.6170006 22.390996,2.3429985 20.12,2.2509995 18.580998,2.3590012 17.940007,2.6829987 17.868993,3.0900002 17.868993,26.970001 18.048009,28.272003 18.58701,29.113998 19.659001,29.697998 21.437016,30.227997 22.149999,30.389 22.467015,30.490997 22.707005,31.401001 22.179997,32 21.910008,31.994003 21.198003,31.928001 15.424992,31.640999 10.755008,31.879997 9.4850186,31.879997 9.1740131,31.305 9.2879972,30.665001 10.011996,30.275002 11.737002,29.695 12.791018,29.005997 13.401005,27.909996 13.605015,26.298996 13.605015,3.0900002 13.390018,2.5390015 12.743014,2.2750015 12.342013,2.2579994 11.329989,2.2509995 7.3779925,2.6469994 4.6229872,3.8320007 3.0840158,5.4970016 1.7250066,8.1439972 1.2399907,8.7959976 0.598999,8.7900009 0.041992187,8.4729996 0,7.9039993 0.62899778,4.7839966 0.83898971,0.59899902 1.2040105,0.20999908 2.2999882,0.14400101 7.7489947,0.39500046 13.748997,0.47900009 19.16602,0.44900131 23.539008,0.35900116 26.867011,0.20999908z"
/>
</g>
</svg>
Then I've used the method .getBBox() to get the size of the bounding boxes for the two icons. In both cases the width and the height for the bounding box is almost 32 and the and the x and y is 0. I've changed the viewBox to x y w h i.e 0 0 32 32. This will scale down the image inside the svg, while the width and the height of the svg element stays at 24/24
I am working on a project in which I am developing a web application where artist can upload there artwork which will be printed on various smartphone phone cases(A small version of websites like RedBubble.com or Society6.com).
So as one of its module, what I want to achieve is:
Admin can upload the phone case shapes(just the back of the case
with transparent camera hole) from admin panel.
Artist can upload the artwork and which will be displayed on all
available phone cases as final product to the customer.
I have searched a lot about this and came out with the following idea but now I am totally lost:
Admin can create shapes using software like InkScape which will give them SVG files and its code.
These files can be stored in the database(content of the file or the file name, I was unable to figure out).
Artist will upload his/her artwork and will be stored in the database as jpg file.
So now, I have both, the shape(SVG) and the artwork(JPG) stored in the database.
I just cannot figure out how to display the final product which will be the selected artwork on the selected phone case shaped SVG. Or am I going totally wrong?
This is what I what to achieve:
http://postimg.org/image/vp267jvp5/
But as you can see that I totally messed it up. Here is the code for the file. SVG was generated from InkScape. I used pattern tags to fit in the image.
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<html>
<body>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="212"
height="360"
id="svg2"
version="1.1"
inkscape:version="0.48.5 r10040"
sodipodi:docname="shit.svg">
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="1"
inkscape:cx="76"
inkscape:cy="265.01282"
inkscape:document-units="px"
inkscape:current-layer="layer1"
showgrid="false"
inkscape:window-width="1366"
inkscape:window-height="691"
inkscape:window-x="-9"
inkscape:window-y="-9"
inkscape:window-maximized="1"
fit-margin-top="0"
fit-margin-left="0"
fit-margin-right="0"
fit-margin-bottom="0" />
<metadata
id="metadata7">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1"
style="display:inline"
transform="translate(-299,-397.375)">
<path
d="m 343,397.375 c -24.376,0 -44,19.624 -44,44 l 0,272 c 0,24.376 19.624,44 44,44 l 124,0 c 24.376,0 44,-19.624 44,-44 l 0,-272 c 0,-24.376 -19.624,-44 -44,-44 l -124,0 z m 18.5,34 c 12.42641,0 22.5,6.71573 22.5,15 0,8.28427 -10.07359,15 -22.5,15 -12.42641,0 -22.5,-6.71573 -22.5,-15 0,-8.28427 10.07359,-15 22.5,-15 z"
fill="url(#img1)"
id="rect2985"
inkscape:connector-curvature="0" />
</g>
<defs>
<filter id="blur">
<feGaussianBlur stdDeviation="0"/>
</filter>
<pattern id="img1" x="0" y="0" patternUnits="userSpaceOnUse" width="212" height="360" >
<image xlink:href="uploads/artwork.jpg" x="0" y="0" width="212" height="360" />
</pattern>
</defs>
</svg>
<img src="uploads/artwork.jpg" >
</body>
</html>
I would appreciate if you can provide me some steps or examples or tutorial links or should I use some library because I searched and the above is all I got. I am using php and mysql database server.
You can achieve what you want by manipulating the SVG by:
Turn the cover shape into a clip path
dynamically load the user's image into the SVG and apply the clip path to it
I've made a demo fiddle here.
Here's the code:
initCover("svg2", "layer1");
replaceCover("svg2", "layer1", "http://lorempixel.com/400/400/");
function initCover(svgId, coverId)
{
// Turn cover into a clipping path
var svg = document.getElementById(svgId);
var cover = svg.getElementById(coverId);
var clipPath = document.createElementNS("http://www.w3.org/2000/svg", "clipPath");
clipPath.setAttribute("id", "cover-clip");
if (cover.getAttribute("transform"))
clipPath.setAttribute("transform", cover.getAttribute("transform"));
// Copy shape from cover to clipPath (assumes cover has only one child element
clipPath.appendChild(cover.firstElementChild.cloneNode());
svg.appendChild(clipPath);
// Optionally hide the original cover shape
cover.style.visibility = "hidden";
}
function replaceCover(svgId, coverId, imageURL)
{
var svg = document.getElementById(svgId);
var cover = svg.getElementById(coverId);
// Get the cover width and height
var coverBox = cover.getBBox();
// Add a new image element for the image
var image = document.createElementNS("http://www.w3.org/2000/svg", "image");
image.setAttributeNS("http://www.w3.org/1999/xlink", "href", imageURL);
// Set image size so that the image fills the cover and is centred
image.setAttribute("width", coverBox.width);
image.setAttribute("height", coverBox.height);
image.setAttribute("preserveAspectRatio", "xMidYMid slice");
image.setAttribute("clip-path", "url(#cover-clip)");
svg.appendChild(image);
}
You can use html5 canvas's compositing ability to draw only into your phone-case
In particular, context.globalCompositeOperation='source-in' will display your artwork only in the non-transparent pixels of your phone case.
Start with this image of your phone case where everything is transparent except the phone case:
Then you can use compositing to draw your artwork only over the black case pixels like this:
// draw the phone case on the canvas
context.drawImage(phonecase,0,0);
// set compositing to source-in
// future drawings will only be visible where existing pixels are opaque
context.globalCompositeOperation='source-in';
// draw the artwork over the phonecase
// compositing will display the artwork only inside the opaque phonecase pixels
context.drawImage(artCanvas,offsetX,offsetY);
// always clean up! Return compositing to its default mode
context.globalCompositeOperation='source-out';
Here's example code and a Demo:
This example adds the ability to resize the artwork and reposition it horizontally and vertically.
Good luck with your project!
var canvas=document.getElementById("canvas");
var ctx=canvas.getContext("2d");
var artCanvas=document.createElement("canvas");
var artCtx=artCanvas.getContext("2d");
var cw,ch,phone,art;
$scaleslider=$('#scaleslider');
$scaleslider.change(function(){ resetScale(); draw(); })
//
$hslider=$('#hslider');
$hslider.change(function(){ draw(); })
//
$vslider=$('#vslider');
$vslider.change(function(){ draw(); });
// image preloader
var imageURLs=[];
imageURLs.push("https://dl.dropboxusercontent.com/u/139992952/multple/case.png");
imageURLs.push("https://dl.dropboxusercontent.com/u/139992952/multple/art.png");
// the loaded images will be placed in imgs[]
var imgs=[];
var imagesOK=0;
loadAllImages(start);
function loadAllImages(callback){
for (var i=0; i<imageURLs.length; i++) {
var img = new Image();
imgs.push(img);
img.onload = function(){
imagesOK++;
if (imagesOK>=imageURLs.length ) {
callback();
}
};
img.onerror=function(){alert("image load failed");}
img.crossOrigin="anonymous";
img.src = imageURLs[i];
}
}
//
function start(){
// the imgs[] array now holds fully loaded images
// user friendly names for images
phone=imgs[0];
art=imgs[1];
// size the on-screen canvas to phone size
cw=canvas.width=phone.width;
ch=canvas.height=phone.height;
// resetScale
resetScale();
// draw the initial rendering
draw();
}
function resetScale(){
var scale=$scaleslider.val()/100;
// calc the scaled size of the artwork
var artW=art.width*scale;
var artH=art.height*scale;
// size the off-screen canvas to allow 2x2 of art images
artCanvas.width=artW*2;
artCanvas.height=artH*2;
// draw a grid of 2x2 art images to allow horizontal
// and vertical repositioning
artCtx.drawImage(art,0,0,artW,artH);
artCtx.drawImage(art,artW,0,artW,artH);
artCtx.drawImage(art,artW,artH,artW,artH);
artCtx.drawImage(art,0,artH,artW,artH);
// set offsets used in horiz & vert repositioning
$hslider.attr('max',artW);
$hslider.val(0);
$vslider.attr('max',artH);
$vslider.val(0);
}
// draw the artwork inside the phone case
// Use the slider values to reposition the artwork
function draw(){
var offsetX=-$hslider.val();
var offsetY=-$vslider.val();
ctx.clearRect(0,0,cw,ch);
ctx.save();
ctx.drawImage(phone,0,0);
ctx.globalCompositeOperation='source-in';
ctx.drawImage(artCanvas,offsetX,offsetY);
ctx.restore();
}
body{ background-color: ivory; }
canvas{border:1px solid red;}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
Scale Artwork<input id=scaleslider type=range min=25 max=150 value=100><br>
Move Horizontally:<input id=hslider type=range min=0 max=200 value=100><br>
Move Vertically:<input id=vslider type=range min=0 max=200 value=100><br>
<canvas id="canvas" width=300 height=300></canvas>