HTML Card Animation: Create a Ghost Card Effect for Your Website in 3 Steps: Free Downloadable Source File and Step-by-Step Explanation :

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

HTML
<!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:

  1. <!DOCTYPE html> – Declares the document as HTML5.
  2. <html lang="en"> – Defines the language as English for accessibility and SEO.
  3. <head> – Contains metadata and links required for the HTML document.
  4. <meta charset="UTF-8"> – Sets the character encoding to UTF-8 for universal character support.
  5. <meta name="viewport" content="width=device-width, initial-scale=1.0"> – Ensures the site is responsive on all devices by setting the viewport.
  6. <title>3D Pencil Drawing Effect</title> – Defines the title of the webpage that appears in the browser tab.
  7. <link rel="stylesheet" href="style.css"> – Links the external CSS file style.css for styling.
  8. <body> – The main content of the document begins here.
  9. <div class="container"> – Creates a container div to center the canvas on the page.
  10. <canvas id="pencilCanvas"></canvas> – Defines a canvas with an ID of pencilCanvas where the drawing effect will appear.
  11. <script src="script.js"></script> – Links to the JavaScript file script.js to control animation.
  12. </body> and </html> – Close the body and HTML tags.

Step 2 : CSS For HTML card animation In Website:

CSS Code

CSS
* {
    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:

  1. 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.
  2. .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.
  3. 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 HTML card animation In Website:

JavaScript Code

JavaScript
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:

  1. const canvas = document.getElementById('pencilCanvas'); – Selects the canvas element by ID.
  2. const ctx = canvas.getContext('2d'); – Gets the 2D drawing context to allow for drawing commands.
  3. canvas.width = 600; – Sets the width of the canvas in pixels.
  4. 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 :

html card

Git Hub link :

3d website

Read our previous blog post :

Animated CSS gallery with smooth view transitions for interactive, responsive design.
Transform Your Website with Stunning CSS Gallery Animations! 🌟

Leave a Comment