Creating a 3D Pencil Drawing Effect on Your Website can captivate your audience and add a unique touch to your design. In this guide, we’ll walk through the process of building a 3D pencil drawing effect using HTML, CSS, and JavaScript. Whether you’re a beginner or an experienced developer, this tutorial will help you add depth and interactivity to your website with an eye-catching 3D effect.
Main focus:
Objective: Learn to design a stunning 3D pencil drawing effect for a website.
Purpose: Add an engaging 3D pencil effect to attract and retain users.
Focus: Easy, step-by-step tutorial covering HTML, CSS, and JavaScript.
Skill Level: Suitable for both beginners and experienced developers.
Outcome: Create a 3D pencil drawing effect with full source code and clear explanations.
By the end of this guide, you’ll have a dynamic 3D pencil drawing feature that enhances your website’s visual appeal and interactivity, making it more engaging for visitors. Let’s dive in and bring this 3D design to life on your site!
What You’ll Learn in This Tutorial
In this tutorial, we’ll guide you through the process of creating a 3D pencil drawing effect from scratch in three simple steps. You’ll learn how to:
Structure the HTML to create the basic layout, including the background whiteboard and black screen.
Style the pencil with CSS to make the 3D pencil drawing visually appealing.
Implement JavaScript functionality to enable a smooth and interactive drawing effect.
Additionally, we’ll provide a free downloadable source file that includes all the necessary code, so you can easily implement the effect on your website.
Step1: HTML For 3D Pencil Drawing Effect In Website:
height: 100vh; – Sets the body height to 100% of the viewport.
margin: 0; – Removes default body margin for full-width display.
background-color: #f4f4f9; – Sets a light gray background color.
.container – Styles the container div.
position: relative; – Positions the container to center within the body.
width: 600px; and height: 400px; – Sets the dimensions of the canvas.
canvas – Styles the canvas element.
width: 100%; and height: 100%; – Makes the canvas fill the container.
background-color: #fff; – Sets a white background for the drawing area.
box-shadow: 0px 4px 10px rgba(0, 0, 0, 0.2); – Adds a shadow to give a 3D effect.
border-radius: 8px; – Rounds the corners of the canvas.
Step 3 : Javascript For 3D Pencil Drawing Effect In Website:
JavaScript Code
JavaScript
(function(window){functionSketch() {this.group =new paper.Group();this.isDrawing =false;// paper.js allows us to use the mouse easily to create vector lines.// more here http://paperjs.org/tutorials/interaction/creating-mouse-tools/this.mouseTool =new paper.Tool();this.mouseTool.minDistance =5;this.mouseTool.maxDistance =30;this.mouseTool.on('mousedown', this.onMouseDown.bind(this));this.mouseTool.on('mousedrag', this.onMouseDrag.bind(this));this.mouseTool.on('mouseup', this.onMouseUp.bind(this)); }Sketch.prototype.onMouseDown=function(e) {this.isDrawing =true;this.currPath =new paper.Path();this.currPath.fillColor ='#424242';this.currPath.add(e.point); }Sketch.prototype.onMouseDrag=function(e) {if (!this.isDrawing) return;if (!e.point.isInside(this.sketchingBounds)){this.onMouseUp(e);return; }var step = e.delta.divide(2); step.angle +=10;var top = e.middlePoint.add(step);var bottom = e.middlePoint.subtract(step);this.currPath.add(top);this.currPath.insert(0, bottom);this.currPath.smooth(10); }Sketch.prototype.onMouseUp=function(e) {if (!this.isDrawing) return;this.isDrawing =false;if (e.point.isInside(this.sketchingBounds)) {this.currPath.add(e.point);this.currPath.closed =true; }this.group.addChild(this.currPath); }Sketch.prototype.setSketchingBounds=function(x, y, width, height) {this.sketchingBounds =new paper.Rectangle(x, y, width, height); } window.Sketch = Sketch;})(window);/* Set up the Pencil */var pencil = (function() {var pencil, isDrawing, rangeX =0, rangeY =0, scale =1.5, canvasW, canvasH, moveRotationRange = {x:100, y:30};functioninit(_canvasW, _canvasH, onReady) { canvasW = _canvasW; canvasH = _canvasH; isDrawing =false; pencil =newTHREE.Object3D();// Load the 3D model and its materialsvar mtlLoader =newTHREE.MTLLoader(); mtlLoader.load( 'https://s3-us-west-2.amazonaws.com/s.cdpn.io/356608/PENCIL.mtl', function( materials ) { materials.preload();var objLoader =newTHREE.OBJLoader(); objLoader.setMaterials( materials ); objLoader.load( 'https://s3-us-west-2.amazonaws.com/s.cdpn.io/356608/PENCIL.obj', function ( object ) {var scale =1.5; object.rotation.x =toRad(90); object.scale.set(scale, scale, scale); pencil.add(object); scene.add( pencil ); });stopDrawing();onReady(); }); }functionstartDrawing() { isDrawing =true; TweenLite.to(pencil.position, 0.2, {z:0, ease: Expo.easeOut}); }functionstopDrawing() { isDrawing =false; TweenLite.to(pencil.position, 0.2, {z:2, ease: Quad.easeInOut}); }functionmove(x, y) { rangeX = (x / wW) -0.5; rangeY = (y / wH) -0.5;// Convert the current mouse position to a point in 3D space. This involves 'unprojecting' the// 2D values into 3D space. More info here: // https://barkofthebyte.azurewebsites.net/post/2014/05/05/three-js-projecting-mouse-clicks-to-a-3d-scene-how-to-do-it-and-how-it-worksvar mouse3D =newTHREE.Vector3( (x / wW) *2-1, -(y / wH) *2+1, 0.5 ); mouse3D.unproject(camera);var dir = mouse3D.sub(camera.position).normalize(); dir.x *= wW / canvasW; dir.y *= wH / canvasH;var distance =- camera.position.z / dir.z;var pos = camera.position.clone().add( dir.multiplyScalar( distance ) ); pencil.position.x = pos.x; pencil.position.y = pos.y; TweenLite.to(pencil.rotation, 0.2, {y: rangeX *toRad(moveRotationRange.x), x: rangeY *toRad(moveRotationRange.y), z:toRad(rangeY *200)}); }// Utility function for converting degrees to radiansfunctiontoRad(deg) {return (Math.PI/180) * deg; }return { init: init, startDrawing: startDrawing, stopDrawing: stopDrawing, move: move,isDrawing:function() {return isDrawing; } }})();// Set up the pencil's shadow. This gives the illusion that the pencil is floating above the pad.// The shadow is actually a 2D vector graphic rendered in the same canvas as the sketch (also using paper.js).// I'm not sure how to get a crisp shadow in THREE.js, which is why I used the 2D canvas to render it.// The shadow rotates when the pencil is moved across the sketchpad to give the illusion that the // light source is at a consistent position (to the top right).// It also gets darker as the pencil is moved closer to the paper.(function(window) {functionShadow() {this.rotationRange =90;this.startAngle =-25;this.grp =new paper.Group();this.grp.applyMatrix =false;this.grp.rotation =this.startAngle;this.grp.pivot =new paper.Point(0, 0);// Load the shadow graphic paper.project.importSVG('https://s3-us-west-2.amazonaws.com/s.cdpn.io/356608/shadow.svg', function(el) { el.position =new paper.Point(0, 85); el.opacity =0.5; el.scale(0.6); el.applyMatrix =false;this.el = el;this.grp.addChild(this.el);this.goFar(); }.bind(this));this.isDragging =false; }Shadow.prototype.move=function(x, y) {if (!this.el) return; rangeX = (x / wW) -0.5;this.grp.rotation =this.startAngle - (this.rotationRange * rangeX);this.grp.position =new paper.Point(x, y); }Shadow.prototype.goNear=function() {if (!this.el) return;this.isDragging =true; TweenLite.to(this.el, 0.2, {opacity:0.4}); TweenLite.to(this.el.position, 0.2, {y:105, ease: Expo.easeOut}); TweenLite.to(this.el.scaling, 0.2, {y:1, ease: Expo.easeOut}); }Shadow.prototype.goFar=function() {if (!this.el) return;this.isDragging =false; TweenLite.to(this.el, 0.2, {opacity:0.1}); TweenLite.to(this.el.position, 0.2, {y:205, ease: Quad.easeInOut}); TweenLite.to(this.el.scaling, 0.2, {y:1.2, ease: Quad.easeInOut}); } window.Shadow = Shadow;})(window)// The main program.// This bit brings all our parts together, the shadow, pencil and drawing mechanicvar wW, wH, pencilCanvas, pencilCanvasW =1920, pencilCanvasH =1080, sketchCanvas, scene, camera, renderer, sketch, sketchpad, shadow, introTl, pencilPos = {x:0, y:0};functioninit() { sketchpad = document.getElementById('sketchpad');// Pencil Renderer pencilCanvas = document.getElementById('pencil3D'); pencilCanvas.setAttribute('width', pencilCanvasW +'px'); pencilCanvas.setAttribute('height', pencilCanvasH +'px'); scene =newTHREE.Scene(); camera =newTHREE.PerspectiveCamera(45, pencilCanvasW / pencilCanvasH, 0.1, 1000); camera.position.z =25; camera.lookAt(scene.position); renderer =newTHREE.WebGLRenderer({ canvas: pencilCanvas, antialias:true, alpha:true }); renderer.setSize(pencilCanvasW, pencilCanvasH);// Light up the pencil, otherwise it would appear boring and flatvar light1 =newTHREE.AmbientLight( 0x404040 ); light1.intensity =6; scene.add(light1);var light2 =newTHREE.SpotLight( 0x404040 ); light2.intensity =3; light2.position.set(-5, 15, 10); light2.target.position =newTHREE.Vector3(0, 0, 5); scene.add(light2);var light3 =newTHREE.SpotLight( 0x404040 ); light3.intensity =0.5; light3.position.set(5, -15, 10); light3.target.position =newTHREE.Vector3(0, 0, 5); scene.add(light3);// Instantiate the sketchpad sketchCanvas = document.getElementById('sketch'); paper.setup(sketchCanvas); sketch =newSketch(); shadow =newShadow(); pencil.init(pencilCanvasW, pencilCanvasH, onReady);onResize();// Some intro animation (the sketchpad expands once everything is loaded) introTl =newTimelineLite({paused:true, delay:2, onComplete:function() {// Allow the user to interact with the mouse only after the intro animation has finished window.addEventListener('mousedown', onMouseDown); window.addEventListener('mouseup', onMouseUp); window.addEventListener('mousemove', onMove); window.addEventListener('resize', onResize); }}); introTl.to('#intro', 0.3, {opacity:0}); introTl.from('#sketchpad', 0.5, {scaleY:0, ease: Expo.easeInOut}); introTl.append(TweenMax.fromTo(pencilPos, 0.7, {x: wW /2, y: wH +300}, {x: wW *0.7, y: wH *0.5, roundProps:'x,y', ease: Expo.easeOut}));render();}functiononReady() { introTl.play();}functiononResize() {// Make the sketchpad responsive wW = window.innerWidth; wH = window.innerHeight;var sketchpadW = (wW -100);var sketchpadH = (wH -100); sketchpad.style.width = sketchpadW +'px'; sketchpad.style.height = sketchpadH +'px';// Make sure the user can't draw outside of the sketchpad bounds sketch.setSketchingBounds(50, 50, sketchpadW, sketchpadH); sketchCanvas.setAttribute('width', wW); sketchCanvas.setAttribute('height', wH); paper.view.viewSize =new paper.Size(wW, wH); paper.view.draw(); pencilCanvas.style.left = ((wW /2) - (pencilCanvasW /2)) +'px'; pencilCanvas.style.top = ((wH /2) - (pencilCanvasH /2)) +'px';}functiononMove(e) {// When the pencil is moved but NOT drawing, make the movement animate at a slightly smoother ratevar duration = pencil.isDrawing() ?0.05:0.25; TweenLite.to(pencilPos, duration, {x: e.clientX, y: e.clientY});}functiononMouseDown(e) { pencil.startDrawing(); shadow.goNear();}functiononMouseUp(e) { pencil.stopDrawing(); shadow.goFar();}functionrender() {requestAnimationFrame(render); renderer.render(scene, camera); pencil.move(pencilPos.x, pencilPos.y); shadow.move(pencilPos.x, pencilPos.y); paper.view.draw();}init();
JavaScript Explanation:
const canvas = document.getElementById('pencilCanvas'); – Selects the canvas element by ID.
const ctx = canvas.getContext('2d'); – Gets the 2D drawing context to allow for drawing commands.
canvas.width = 600; – Sets the width of the canvas in pixels.
canvas.height = 400; – Sets the height of the canvas in pixels.
Pencil Position and Speed Variables: 5. let x = 50; – Initializes the starting x-position of the pencil. 6. let y = 50; – Initializes the starting y-position of the pencil. 7. const speedX = 2; – Sets the horizontal speed of the pencil. 8. const speedY = 1.5; – Sets the vertical speed of the pencil.
drawPencil Function: 9. function drawPencil() { – Begins the function to draw the pencil. 10. ctx.clearRect(0, 0, canvas.width, canvas.height); – Clears the canvas each frame to avoid overlapping drawings. 11. ctx.beginPath(); – Starts a new drawing path for the line. 12. ctx.moveTo(50, 50); – Sets the line’s starting point. 13. ctx.lineTo(x, y); – Draws the line to the current (x, y) position. 14. ctx.strokeStyle = '#000'; – Sets the line color to black. 15. ctx.lineWidth = 2; – Sets the line thickness. 16. ctx.stroke(); – Renders the line on the canvas.
Draw Pencil Tip: 17. ctx.beginPath(); – Starts a new path for the pencil tip. 18. ctx.arc(x, y, 5, 0, Math.PI * 2, false); – Draws a circle at (x, y) for the pencil tip. 19. ctx.fillStyle = '#333'; – Fills the circle with dark gray color. 20. ctx.fill(); – Fills in the color for the pencil tip circle.
Position Update: 21. x += speedX; – Moves the pencil to the right. 22. y += speedY; – Moves the pencil downward. 23. if (x > canvas.width || y > canvas.height) { – Checks if the pencil moves off the canvas. 24. x = 50; – Resets x position if the pencil goes off-screen. 25. y = 50; – Resets y position if the pencil goes off-screen.
Animation Loop: 26. function animate() { – Begins the animation function. 27. drawPencil(); – Calls drawPencil to draw a frame. 28. requestAnimationFrame(animate); – Recursively calls animate to create an animation loop. 29. animate(); – Starts the animation by calling animate.