I recently started drawing on my iPad using the Procreate app with Apple Pencil. I’m enjoying the flexibility of drawing this way. What usually keeps me from painting at home are basic things, like setup, cleaning brushes, proper ventilation, and other factors not really tied to the painting itself. Procreate does a pretty nice job of emulating painting and drawing processes, but adding digital features like undo/redo, layers, and layer effects.
Here’s a Procreate painting I made that I wound up exporting and animating on the web.
See the Pen
Zebra Page- web animation from a Procreate drawing by Sarah Drasner (@sdras)
on CodePen.
You can do this too! There are two basic animation effects we’ll cover here: the parallax effect that takes place on hover (with the ability to turn it off for those with vestibular disorders), and the small drawing effect when the page loads.
Parallax with drawing layers
I mentioned that part of the reason I enjoy drawing on the iPad is the ability to work in layers. When creating layers, I take care to keep certain “themes” on the same layer, for instance, the zebra stripes are on one layer and the dots are on own layer under beneath the stripes.
I’ll extend the drawing beyond the boundaries of where the line from the layer above ends, mainly because you’ll be able to peek around it a bit as we move the drawing around in the parallax effect. If the lines are sharp at any point, this will look unnatural.
Once I’m done creating my layers, I can export things as a Photoshop (PSD) file, thanks to Procreate’s exporting options.
Then I’ll join together a few, so that I’m only working with about 8 layers at most. I use a photoshop plugin called tinyPNG to export each layer individually. I’ve heard there are better compression tools, but I’ve been pretty happy with this one.
Next, I’ll go into my code editor and create a div to house all the various images that are contained in the layers. I give that div relative positioning while all of the images inside it get absolute positioning. This places the images one on top the other.
<div id="zebra-ill" role="presentation">
<img class="zebraimg" src='https://s3-us-west-2.amazonaws.com/s.cdpn.io/28963/zebraexport6.png' />
<img class="zebraimg" src='https://s3-us-west-2.amazonaws.com/s.cdpn.io/28963/zebraexport5.png' />
…
</div>
#zebra-ill {
position: relative;
min-height: 650px;
max-width: 500px;
}
.zebraimg {
position: absolute;
top: 0;
left: 0;
perspective: 600px;
transform-style: preserve-3d;
transform: translateZ(0);
width: 100%;
}
The 100% width on the image will confines all of the images to the size of the parent div. I do this so that I’m controlling them all at once with the same restrictions, which works well for responsive conditions. The max-width
and min-height
on the parent allow me to confine the way that the div shrinks and grows, especially as when it gets dropped into a CSS Grid layout. It will need to be flexible, but have some constraints as well and CSS Grid is great for that.
Next, I add a mousemove
event listener on the parent div with JavaScript. That lets me capture some information about the coordinates of the mouse using e.clientX
and e.clientY
.
const zebraIll = document.querySelector('#zebra-ill')
// Hover
zebraIll.addEventListener('mousemove', e => {
let x = e.clientX;
let y = e.clientY;
})
Then, I’ll go through each of the drawings and use those coordinates to move the images around. I’ll even apply transform styles connected to those coordinates.
const zebraIll = document.querySelector('#zebra-ill')
const zebraIllImg = document.querySelectorAll('.zebraimg')
const rate = 0.05
//hover
zebraIll.addEventListener('mousemove', e => {
let x = e.clientX;
let y = e.clientY;
zebraIllImg.forEach((el, index) => {
el.style.transform =
`rotateX(${x}deg) rotateY(${y}deg)`
})
})
See the Pen
zebra page by Sarah Drasner (@sdras)
on CodePen.
Woah, slow down there partner! That’s way too much movement, we want something a little more subtle. So I’ll need to slow it way down by multiplying it by a low rate, like 0.05. I also want to change it a just bit per layer, so I’ll use the layers index to speed up or slow down the movement.
const zebraIll = document.querySelector('#zebra-ill')
const zebraIllImg = document.querySelectorAll('.zebraimg')
const rate = 0.05
// Hover
zebraIll.addEventListener('mousemove', e => {
let x = e.clientX;
let y = e.clientY;
zebraIllImg.forEach((el, index) => {
let speed = index += 1
let xPos = speed + rate * x
let yPos = speed + rate * y
el.style.transform =
`rotateX(${xPos - 20}deg) rotateY(${yPos - 20}deg) translateZ(${index * 10}px)`
})
})
Finally, I can create a checkbox that asks the user if want to turn off this effect.
<p>
<input type="checkbox" name="motiona11y" id="motiona11y" />
<label for="motiona11y">If you have a vestibular disorder, check this to turn off some of the effects</label>
</p>
const zebraIll = document.querySelector('#zebra-ill')
const zebraIllImg = document.querySelectorAll('.zebraimg')
const rate = 0.05
const motioncheck = document.getElementById('motiona11y')
let isChecked = false
// Check to see if someone checked the vestibular disorder part
motioncheck.addEventListener('change', e => {
isChecked = e.target.checked;
})
// Hover
zebraIll.addEventListener('mousemove', e => {
if (isChecked) return
let x = e.clientX;
let y = e.clientY;
// ...
})
Now the user has the ability to look at the layered dimensionality of the drawing on hover, but can also turn the effect off if it is bothersome.
Drawing Effect
The ability to make something look like it’s been drawn on to the page has been around for a while and there are a lot of articles on how it’s done. I cover it as well in a course I made for Frontend Masters.
The premise goes like this:
- Take an SVG path and make it dashed with
dashoffset
. - Make the dash the entire length of the shape.
- Animate the
dashoffset
(the space between dashes).
What you get in the end is a kind of “drawn-on” effect.
But in this particular drawing you might have noticed that the parts I animated look like they were hand-drawn, which is a little more unique. You see, though that effect will work nicely for more mechanical drawings, the web doesn’t quite yet support the use of tapered lines (lines that vary in thickness, as is typical of a more hand-drawn feel).
For this approach, I brought the file into Illustrator, traced the lines from that part of my drawing, and made those lines tapered by going into the Stroke panel, where I selected “More options” and clicked the tapered option from the dropdown.
I duplicated those lines, and created fatter, uniform paths underneath. I then took those fat lines and animate them onto the page. Now my drawing shows through the shape:
Here’s what I did:
- I traced with Pen tool and used tapered brush.
- I duplicated the layer and changed the lines to be uniform and thicker.
- I took the first layer and created a compound path.
- I simplified path points.
- I created clipping mask.
From there, I can animate everything with drawSVG and GreenSock. Though you don’t need to, you could use CSS for this kind of animation. There’s a ton of path points so in this case, so it makes sense to use something more powerful. I wrote another post that goes into depth on how to start off creating these kinds of animations. I would recommend you start there if you’re fresh to it.
To use drawSVG, we need to do a few things:
- Load the plugin script.
- Register the plugin at the top of the JavaScript file.
- Make sure that paths are being used, and that there are strokes on those paths.
- Make sure that those paths are targeted rather than the groups that house them. The parent elements could be targeted instead.
Here’s a very basic example of drawSVG (courtesy of GreenSock):
See the Pen
DrawSVGPlugin Values by GreenSock (@GreenSock)
on CodePen.
So, in the graphics editor, there is a clipping mask with more artful lines, that expose fat uniform lines underneath. From here, we’ll grab a hold of those thicker paths and use the drawSVG plugin to animate them onto the page.
//register the plugin
gsap.registerPlugin(DrawSVGPlugin);
const drawLines = () => {
gsap.set('.cls-15, #yellowunderline, .cls-13', {
visibility: 'visible'
})
const timeline = gsap.timeline({
defaults: {
delay: 1,
ease: 'circ',
duration: 2
}
})
.add('start')
.fromTo('.cls-15 path', {
drawSVG: '0%'
}, {
drawSVG: '100%',
immediateRender: true
}, 'start')
.fromTo('#yellowunderline path', {
drawSVG: '50% 50%'
}, {
drawSVG: '100%',
immediateRender: true
}, 'start+=1')
.fromTo('.cls-13', {
drawSVG: '50% 50%'
}, {
drawSVG: '100%',
immediateRender: true
}, 'start+=1')
}
window.onload = () => {
drawLines()
};
And there we have it! An initial illustration for our site that’s created from a layered drawing in the Procreate iPad app. I hope this gets you going making your web projects unique with wonderful hand-drawn illustrations. If you make anything cool, let us know in the comments below!