Table of Contents
Introduction:
Bring Your Creativity to Life with a Stunning 3D Ghost Rider HTML Card Animation!
This project will guide you through creating a visually striking 3D Ghost Rider-themed HTML card animation, blending cutting-edge technologies like HTML, CSS, and JavaScript with the powerful Three.js library.
Whether you’re an experienced web developer or a curious tech enthusiast eager to dive into advanced animation techniques, this step-by-step guide will help you craft an interactive and engaging HTML card animation that truly stands out.
Why Create a 3D HTML Animated Card?
Animated HTML cards are an excellent way to engage users and showcase both creativity and technical prowess. This project combines 3D rendering, glowing effects, and real-time user customization to deliver a modern, interactive experience that makes your HTML card come to life.
Setup and Requirements:
Before we dive into creating the 3D Ghost Rider HTML card, let’s make sure you have everything set up for a smooth development process.
1. Prerequisites
To get started, you should have a basic understanding of the following:
- HTML: For structuring the card layout.
- CSS: For styling the HTML card and giving it the desired visual effects.
- JavaScript: For adding interactivity and animations to the card.
- Familiarity with Three.js is a bonus but not mandatory, as this guide will walk you through its usage.
2. Required Tools
Make sure you have these tools ready for efficient development:
- A text editor like VS Code to write and organize your code.
- A browser (e.g., Chrome or Firefox) to preview your HTML card animation.
- A stable internet connection to load external libraries like Three.js and dat.GUI from a CDN.
3. Libraries Used
This project makes use of the following libraries to achieve its advanced visual effects:
- Three.js: A powerful JavaScript library used for rendering 3D models, animations, and effects within the HTML card.
- dat.GUI: A lightweight GUI controller that allows users to interact with the HTML card’s properties, such as adjusting bloom effects, lighting, and other animation properties in real time.
Project Overview:
Let’s take a look at what we’ll be building in this project:
User-Customized GUI Controls: Incorporate dat.GUI to enable users to adjust animation properties, such as the intensity of the glowing effects or the rotation speed of the card.
Dark-Themed 3D HTML Animated Card: Create an interactive Ghost Rider-themed HTML card animation that features a mysterious glow and sleek modern design.
Three.js for 3D Rendering: Use Three.js to seamlessly render 3D elements and add stunning effects to the HTML card.
Visual Enhancements: Add dynamic bloom effects to make the HTML card glow, highlighting the 3D features and enhancing its visual appeal.
By the end of this project, you’ll have a fully functional, interactive 3D HTML card animation that will captivate users and provide you with a deeper understanding of how to implement Three.js for real-world applications.
Step1: HTML For HTML card In Website:
HTML Code
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="style.css">
<script defer src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r121/three.min.js"></script>
<script defer src="https://cdnjs.cloudflare.com/ajax/libs/dat-gui/0.7.7/dat.gui.min.js"></script>
<script defer type="module" src="main.js"></script>
<link rel="shortcut icon" href="./final upscale.png" type="image/x-icon">
<title>Ghost_rider card animation</title>
</head>
<body>
<div id="world"></div>
</body>
</html>
HTML Explanation:
<!DOCTYPE html>
– Declares the document as HTML5.<html lang="en">
– Defines the language as English for accessibility and SEO.<head>
– Contains metadata and links required for the HTML document.<meta charset="UTF-8">
– Sets the character encoding to UTF-8 for universal character support.<meta name="viewport" content="width=device-width, initial-scale=1.0">
– Ensures the site is responsive on all devices by setting the viewport.<title>3D Pencil Drawing Effect</title>
– Defines the title of the webpage that appears in the browser tab.<link rel="stylesheet" href="style.css">
– Links the external CSS filestyle.css
for styling.<body>
– The main content of the document begins here.<div class="container">
– Creates a container div to center the canvas on the page.<canvas id="pencilCanvas"></canvas>
– Defines a canvas with an ID ofpencilCanvas
where the drawing effect will appear.<script src="script.js"></script>
– Links to the JavaScript filescript.js
to control animation.</body>
and</html>
– Close the body and HTML tags.
Step 2 : CSS For HTML card animation In Website:
CSS Code
* {
margin: 0;
padding: 0;
box-sizing: border-box;
/* background: black; */
/* text-align: center; */
outline: none;
}
#world {
width: 100%;
height: 100vh;
background: black;
}
canvas {
left: calc(50% - 325px);
position: absolute;
}
CSS Explanation:
body
– Styles the main body of the page.display: flex;
– Uses flexbox to align items within the body.justify-content: center;
– Horizontally centers content.align-items: center;
– Vertically centers content.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;
andheight: 400px;
– Sets the dimensions of the canvas.
canvas
– Styles the canvas element.width: 100%;
andheight: 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 HTML card animation In Website:
JavaScript Code
import { EffectComposer } from "https://unpkg.com/three@0.120.0/examples/jsm/postprocessing/EffectComposer.js";
import { RenderPass } from "https://unpkg.com/three@0.120.0/examples/jsm/postprocessing/RenderPass.js";
import { UnrealBloomPass } from "https://unpkg.com/three@0.120.0/examples/jsm/postprocessing/UnrealBloomPass.js";
import { OBJLoader } from "https://unpkg.com/three@0.120.0/examples/jsm/loaders/OBJLoader";
import { OrbitControls } from "https://unpkg.com/three@0.120.0/examples/jsm/controls/OrbitControls";
var cardtemplate = "https://raw.githubusercontent.com/pizza3/asset/master/cardtemplate3.png";
var cardtemplateback = "https://raw.githubusercontent.com/pizza3/asset/master/cardtemplateback4.png";
var flower = "https://raw.githubusercontent.com/pizza3/asset/master/flower3.png";
var noise2 = "https://raw.githubusercontent.com/pizza3/asset/master/noise2.png";
var color11 = "https://raw.githubusercontent.com/pizza3/asset/master/color11.png";
var backtexture = "https://raw.githubusercontent.com/pizza3/asset/master/color3.jpg";
var skullmodel = "https://raw.githubusercontent.com/pizza3/asset/master/skull5.obj";
var voronoi = "https://raw.githubusercontent.com/pizza3/asset/master/rgbnoise2.png";
var scene,
sceneRTT,
camera,
cameraRTT,
renderer,
container,
width = 1301,
height = window.innerHeight,
frontmaterial,
backmaterial,
controls,
bloomPass,
composer,
frontcard,
backcard;
var options = {
exposure: 2.8,
bloomStrength: 0.8,
bloomThreshold: 0,
bloomRadius: 1.29,
color0: [197, 81, 245],
color1: [65, 0, 170],
color2: [0, 150, 255],
isanimate: false,
};
var gui = new dat.GUI();
var bloom = gui.addFolder("Bloom");
bloom.add(options, "bloomStrength", 0.0, 5.0).name("bloomStrength").listen();
bloom.add(options, "bloomRadius", 0.1, 2.0).name("bloomRadius").listen();
bloom.open();
var color = gui.addFolder("Colors");
color.addColor(options, "color0").name("Border");
color.addColor(options, "color1").name("Base");
color.addColor(options, "color2").name("Eye");
color.open();
var isanim = gui.addFolder("Animate");
isanim.add(options, "isanimate").name("Animate");
isanim.open();
gui.close()
const vert = `
varying vec2 vUv;
varying vec3 camPos;
varying vec3 eyeVector;
varying vec3 vNormal;
void main() {
vUv = uv;
camPos = cameraPosition;
vNormal = normal;
vec4 worldPosition = modelViewMatrix * vec4( position, 1.0);
eyeVector = normalize(worldPosition.xyz - abs(cameraPosition));
gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
}
`;
const fragPlane = `
varying vec2 vUv;
uniform sampler2D skullrender;
uniform sampler2D cardtemplate;
uniform sampler2D backtexture;
uniform sampler2D noiseTex;
uniform sampler2D color;
uniform sampler2D noise;
uniform vec4 resolution;
varying vec3 camPos;
varying vec3 eyeVector;
varying vec3 vNormal;
float Fresnel(vec3 eyeVector, vec3 worldNormal) {
return pow( 1.0 + dot( eyeVector, worldNormal), 1.80 );
}
void main() {
vec2 uv = gl_FragCoord.xy/resolution.xy ;
vec4 temptex = texture2D( cardtemplate, vUv);
vec4 skulltex = texture2D( skullrender, uv - 0.5 );
gl_FragColor = temptex;
float f = Fresnel(eyeVector, vNormal);
vec4 noisetex = texture2D( noise, mod(vUv*2.,1.));
if(gl_FragColor.g >= .5 && gl_FragColor.r < 0.6){
gl_FragColor = f + skulltex;
gl_FragColor += noisetex/5.;
} else {
vec4 bactex = texture2D( backtexture, vUv);
float tone = pow(dot(normalize(camPos), normalize(bactex.rgb)), 1.);
vec4 colortex = texture2D( color, vec2(tone,0.));
//sparkle code, dont touch this!
vec2 uv2 = vUv;
vec3 pixeltex = texture2D(noiseTex,mod(uv*5.,1.)).rgb;
float iTime = 1.*0.004;
uv.y += iTime / 10.0;
uv.x -= (sin(iTime/10.0)/2.0);
uv2.y += iTime / 14.0;
uv2.x += (sin(iTime/10.0)/9.0);
float result = 0.0;
result += texture2D(noiseTex, mod(uv*4.,1.) * 0.6 + vec2(iTime*-0.003)).r;
result *= texture2D(noiseTex, mod(uv2*4.,1.) * 0.9 + vec2(iTime*+0.002)).b;
result = pow(result, 10.0);
gl_FragColor *= colortex;
gl_FragColor += vec4(sin((tone + vUv.x + vUv.y/10.)*10.))/8.;
// gl_FragColor += vec4(108.0)*result;
}
gl_FragColor.a = temptex.a;
}
`;
const fragPlaneback = `
varying vec2 vUv;
uniform sampler2D skullrender;
uniform sampler2D cardtemplate;
uniform sampler2D backtexture;
uniform sampler2D noiseTex;
uniform sampler2D color;
uniform sampler2D noise;
uniform vec4 resolution;
varying vec3 camPos;
varying vec3 eyeVector;
varying vec3 vNormal;
float Fresnel(vec3 eyeVector, vec3 worldNormal) {
return pow( 1.0 + dot( eyeVector, worldNormal), 1.80 );
}
void main() {
vec2 uv = gl_FragCoord.xy/resolution.xy ;
vec4 temptex = texture2D( cardtemplate, vUv);
vec4 skulltex = texture2D( skullrender, vUv );
gl_FragColor = temptex;
vec4 noisetex = texture2D( noise, mod(vUv*2.,1.));
float f = Fresnel(eyeVector, vNormal);
vec2 uv2 = vUv;
vec3 pixeltex = texture2D(noiseTex,mod(uv*5.,1.)).rgb;
float iTime = 1.*0.004;
uv.y += iTime / 10.0;
uv.x -= (sin(iTime/10.0)/2.0);
uv2.y += iTime / 14.0;
uv2.x += (sin(iTime/10.0)/9.0);
float result = 0.0;
result += texture2D(noiseTex, mod(uv*4.,1.) * 0.6 + vec2(iTime*-0.003)).r;
result *= texture2D(noiseTex, mod(uv2*4.,1.) * 0.9 + vec2(iTime*+0.002)).b;
result = pow(result, 10.0);
vec4 bactex = texture2D( backtexture, vUv);
float tone = pow(dot(normalize(camPos), normalize(bactex.rgb)), 1.);
vec4 colortex = texture2D( color, vec2(tone,0.));
if(gl_FragColor.g >= .5 && gl_FragColor.r < 0.6){
float tone = pow(dot(normalize(camPos), normalize(skulltex.rgb)), 1.);
vec4 colortex2 = texture2D( color, vec2(tone,0.));
if(skulltex.a > 0.2){
gl_FragColor = colortex;
// gl_FragColor += vec4(108.0)*result;
// gl_FragColor += vec4(sin((tone + vUv.x + vUv.y/10.)*10.))/8.;
} else {
gl_FragColor = vec4(0.) + f;
gl_FragColor += noisetex/5.;
}
gl_FragColor += noisetex/5.;
} else {
//sparkle code, dont touch this!
gl_FragColor *= colortex;
gl_FragColor += vec4(sin((tone + vUv.x + vUv.y/10.)*10.))/8.;
}
}
`;
const vertskull = `
varying vec3 vNormal;
varying vec3 camPos;
varying vec3 vPosition;
varying vec2 vUv;
varying vec3 eyeVector;
void main() {
vNormal = normal;
vUv = uv;
camPos = cameraPosition;
vPosition = position;
vec4 worldPosition = modelViewMatrix * vec4( position, 1.0);
eyeVector = normalize(worldPosition.xyz - cameraPosition);
gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
}
`;
const fragskull = `
#define NUM_OCTAVES 5
uniform vec4 resolution;
varying vec3 vNormal;
varying vec3 vPosition;
uniform float time;
varying vec3 camPos;
varying vec2 vUv;
uniform vec3 color1;
uniform vec3 color0;
varying vec3 eyeVector;
float rand(vec2 n) {
return fract(sin(dot(n, vec2(12.9898, 4.1414))) * 43758.5453);
}
float noise(vec2 p){
vec2 ip = floor(p);
vec2 u = fract(p);
u = u*u*(3.0-2.0*u);
float res = mix(
mix(rand(ip),rand(ip+vec2(1.0,0.0)),u.x),
mix(rand(ip+vec2(0.0,1.0)),rand(ip+vec2(1.0,1.0)),u.x),u.y);
return res*res;
}
float fbm(vec2 x) {
float v = 0.0;
float a = 0.5;
vec2 shift = vec2(100);
// Rotate to reduce axial bias
mat2 rot = mat2(cos(0.5), sin(0.5), -sin(0.5), cos(0.50));
for (int i = 0; i < NUM_OCTAVES; ++i) {
v += a * noise(x);
x = rot * x * 2.0 + shift;
a *= 0.5;
}
return v;
}
float setOpacity(float r, float g, float b) {
float tone = (r + g + b) / 3.0;
float alpha = 1.0;
if(tone<0.69) {
alpha = 0.0;
}
return alpha;
}
vec3 rgbcol(float r, float g, float b) {
return vec3(r/255.0,g/255.0,b/255.0);
}
float Fresnel(vec3 eyeVector, vec3 worldNormal) {
return pow( 1.0 + dot( eyeVector, worldNormal), 3.0 );
}
void main() {
vec2 olduv = gl_FragCoord.xy/resolution.xy ;
float f = Fresnel(eyeVector, vNormal);
float gradient2 = (f)*(.3 - vPosition.y) ;
float scale = 8.;
// olduv *= 0.5;
// olduv.y -= 0.5;
olduv.y = olduv.y - time;
vec2 p = olduv*scale;
float noise = fbm( p + time );
vec2 uv = gl_FragCoord.xy/resolution.xy ;
// uv = normalize( vNormal ).xy ;
vec3 newCam = vec3(0.,5.,10.);
float gradient = dot(.0 - normalize( newCam ), normalize( vNormal )) ;
vec3 viewDirectionW = normalize(camPos - vPosition);
float fresnelTerm = dot(viewDirectionW, vNormal);
fresnelTerm = clamp( 1. - fresnelTerm, 0., 1.) ;
vec3 color = vec3(noise) + gradient;
vec3 color2 = color - 0.2;
float noisetone = setOpacity(color.r,color.g,color.b);
float noisetone2 = setOpacity(color2.r,color2.g,color2.b);
vec4 backColor = vec4(color, 1.);
backColor.rgb = rgbcol(color0.r,color0.g,color0.b)*noisetone;
// backColor.a = noisetone;
vec4 frontColor = vec4(color2, 1.);
frontColor.rgb = rgbcol(color1.r,color1.g,color1.b)*noisetone;
// frontColor.a = noisetone2;
if(noisetone2>0.0){
// show first color
gl_FragColor = frontColor;
} else {
// show 2nd color
gl_FragColor = backColor;
}
}
`;
function init() {
container = document.getElementById("world");
camera = new THREE.PerspectiveCamera(
30,
1301 / 2 / window.innerHeight,
1,
10000
);
camera.position.z = 100;
cameraRTT = new THREE.PerspectiveCamera(
30,
1301 / 2 / window.innerHeight,
1,
10000
);
// cameraRTT.position.z = 70;
// cameraRTT.position.y = -14.5;
cameraRTT.position.z = 30;
cameraRTT.position.y = -3.5;
scene = new THREE.Scene();
sceneRTT = new THREE.Scene();
renderer = new THREE.WebGLRenderer({ antialias: true, autoSize: true });
renderer.setPixelRatio(2);
renderer.setSize(1301 / 2, window.innerHeight);
renderer.autoClear = false;
renderer.shadowMap.type = THREE.PCFSoftShadowMap;
renderer.interpolateneMapping = THREE.ACESFilmicToneMapping;
renderer.outputEncoding = THREE.sRGBEncoding;
controls = new OrbitControls(camera, renderer.domElement);
controls.enableZoom = false;
controls.update();
document.getElementById("world").appendChild(renderer.domElement);
//
var renderScene = new RenderPass(sceneRTT, cameraRTT);
bloomPass = new UnrealBloomPass(
new THREE.Vector2(1301, window.innerHeight),
0.7,
0.4,
0.85
);
composer = new EffectComposer(renderer);
composer.renderToScreen = false;
composer.addPass(renderScene);
composer.addPass(bloomPass);
//
plane();
planeback();
loadskull();
animate();
}
function plane() {
var geometry = new THREE.PlaneGeometry(20, 30);
frontmaterial = new THREE.ShaderMaterial({
uniforms: {
cardtemplate: {
type: "t",
value: new THREE.TextureLoader().load(cardtemplate),
},
backtexture: {
type: "t",
value: new THREE.TextureLoader().load(backtexture),
},
noise: {
type: "t",
value: new THREE.TextureLoader().load(noise2),
},
skullrender: {
type: "t",
value: composer.readBuffer.texture,
},
resolution: {
value: new THREE.Vector2(1301 / 2, window.innerHeight),
},
noiseTex: {
type: "t",
value: new THREE.TextureLoader().load(voronoi),
},
color: {
type: "t",
value: new THREE.TextureLoader().load(color11),
},
},
fragmentShader: fragPlane,
vertexShader: vert,
transparent: true,
depthWrite: false,
});
frontcard = new THREE.Mesh(geometry, frontmaterial);
scene.add(frontcard);
}
function planeback() {
var geometry = new THREE.PlaneGeometry(20, 30);
backmaterial = new THREE.ShaderMaterial({
uniforms: {
cardtemplate: {
type: "t",
value: new THREE.TextureLoader().load(cardtemplateback),
},
backtexture: {
type: "t",
value: new THREE.TextureLoader().load(backtexture),
},
noise: {
type: "t",
value: new THREE.TextureLoader().load(noise2),
},
skullrender: {
type: "t",
value: new THREE.TextureLoader().load(flower),
},
resolution: {
value: new THREE.Vector2(1301 / 2, window.innerHeight),
},
noiseTex: {
type: "t",
value: new THREE.TextureLoader().load(voronoi),
},
color: {
type: "t",
value: new THREE.TextureLoader().load(color11),
},
},
fragmentShader: fragPlaneback,
vertexShader: vert,
transparent: true,
depthWrite: false,
});
backcard = new THREE.Mesh(geometry, backmaterial);
backcard.rotation.set(0, Math.PI, 0);
scene.add(backcard);
}
var eye,
eye2,
basicmat,
skullmaterial,
modelgroup = new THREE.Group();
function loadskull() {
skullmaterial = new THREE.ShaderMaterial({
uniforms: {
time: {
type: "f",
value: 0.0,
},
color1: {
value: new THREE.Vector3(...options.color1),
},
color0: {
value: new THREE.Vector3(...options.color0),
},
resolution: {
value: new THREE.Vector2(1301, window.innerHeight),
},
},
fragmentShader: fragskull,
vertexShader: vertskull,
// depthWrite: false,
});
var spheregeo = new THREE.SphereGeometry(1.5, 32, 32);
basicmat = new THREE.MeshBasicMaterial();
basicmat.color.setRGB(...options.color2);
eye = new THREE.Mesh(spheregeo, basicmat);
eye2 = new THREE.Mesh(spheregeo, basicmat);
eye.position.set(-2.2, -2.2, -6.6);
eye2.position.set(2.2, -2.2, -6.6);
modelgroup = new THREE.Object3D();
modelgroup.add(eye);
modelgroup.add(eye2);
var objloader = new OBJLoader();
objloader.load(skullmodel, function (object) {
var mesh2 = object.clone();
mesh2.position.set(0, 0, -10);
mesh2.rotation.set(Math.PI, 0, Math.PI);
mesh2.children.forEach((val, key) => {
val.traverse(function (child) {
child.geometry = new THREE.Geometry().fromBufferGeometry(
child.geometry
);
child.geometry.mergeVertices();
child.material = skullmaterial;
child.verticesNeedUpdate = true;
child.normalsNeedUpdate = true;
child.uvsNeedUpdate = true;
child.material.flatShading = THREE.SmoothShading;
child.geometry.computeVertexNormals();
});
mesh2.scale.set(8, 8, 8);
modelgroup.add(mesh2);
sceneRTT.add(modelgroup);
});
});
}
var matrix = new THREE.Matrix4();
var period = 5;
var clock = new THREE.Clock();
function updateDraw(deltaTime) {
modelgroup.rotation.set(-camera.rotation._x, -camera.rotation._y, 0);
if (options.isanimate) {
matrix.makeRotationY((clock.getDelta() * 0.7 * Math.PI) / period);
camera.position.applyMatrix4(matrix);
camera.lookAt(frontcard.position);
}
bloomPass.threshold = options.bloomThreshold;
bloomPass.strength = options.bloomStrength;
bloomPass.radius = options.bloomRadius;
if (skullmaterial) {
skullmaterial.uniforms.time.value = deltaTime / 4000;
skullmaterial.uniforms.color1.value = new THREE.Vector3(...options.color1);
skullmaterial.uniforms.color0.value = new THREE.Vector3(...options.color0);
eye2.material.color.setRGB(...options.color2);
eye.material.color.setRGB(...options.color2);
}
}
function animate(deltaTime) {
requestAnimationFrame(animate);
updateDraw(deltaTime);
composer.render();
renderer.render(scene, camera);
}
function handleResize() {
camera.aspect = 1301 / 2 / window.innerHeight;
camera.updateProjectionMatrix();
frontcard.material.uniforms.resolution.value = new THREE.Vector2(
1301 / 2,
window.innerHeight
);
skullmaterial.uniforms.resolution.value = new THREE.Vector2(
1301,
window.innerHeight
);
renderer.setPixelRatio(2);
renderer.setSize(1301 / 2, window.innerHeight);
}
window.addEventListener("load", init, false);
window.addEventListener("resize", handleResize, false);
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
.
Live Preview the hosted website:
Download complete code :

Git Hub link :

Read our previous blog post :
