Trendy Image Trail Mouse Follow

11.22.2023
by
Web Bae

Learn how to create a stunning image trail effect using p5.js in Webflow. This step-by-step JavaScript tutorial covers everything from setup to implementation, perfect for enhancing your website's interactivity and user experience. Boost your site’s engagement with dynamic animations today.

DEMO/CLONE
CODE
TUTORIAL

See the Pen Splide Slider by learyjk (@learyjk) on CodePen.

When it comes to creating a visual experience that grabs attention and feels dynamic, JavaScript paired with libraries like p5.js can be your best friend. Let’s not beat around the bush—animations like image trails can turn a mundane website into an interactive playground. Today, I’m diving into how you can craft a mesmerizing image trail effect using p5.js in Webflow. Trust me, this is the kind of feature that takes your project from “just another website” to something memorable.

Why an Image Trail?

First off, why would you even want an image trail? Well, if you're in the business of creating experiences rather than just websites, you need elements that make users want to interact. An image trail is one of those subtle yet impactful details that can make your site feel more engaging. When executed well, it can give a sense of fluidity and responsiveness that static pages just can't achieve.

The Setup: Integrating p5.js with Webflow

Now, Webflow is great for designers, but when you want to push the boundaries of what’s possible, you need to get your hands dirty with some custom code. Here, we’re leveraging p5.js, a JavaScript library that makes creative coding a breeze.

You start by setting up a div with a class of full, which serves as the parent container for our canvas. The canvas is where all the magic happens, so it needs to be full-screen and ready to adapt to any viewport changes. I loaded p5.js via a CDN directly into the head tag of the Webflow project—keeping it lean and modular.

<div class="full" id="canvas-parent"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.4.0/p5.js"></script>

The Code: Building the Image Trail

Let’s get to the meat of it—the JavaScript that powers this effect. Here’s the code in all its glory:

// CONSTANTS
let imageUrls = [
  "https://cdn.prod.website-files.com/649cd036c3448ae14ea25a03/649cd26dc3448ae14ea35880_pexels-aliakbar-nosrati-15654407.jpg",
  "https://cdn.prod.website-files.com/649cd036c3448ae14ea25a03/649cd1c740f369d4f54540f4_pexels-godisable-jacob-993868.jpg",
  "https://cdn.prod.website-files.com/649cd036c3448ae14ea25a03/649cd1ca2cc144816adbe4b7_pexels-konstantin-mishchenko-2010812.jpg",
  "https://cdn.prod.website-files.com/649cd036c3448ae14ea25a03/649cd1ce12517504410409cf_pexels-andy-coffie-16864841.jpg",
  "https://cdn.prod.website-files.com/649cd036c3448ae14ea25a03/649cd1ec9f40c0296d9dafbb_pexels-harsh-kushwaha-1689731.jpg",
];
// distance mouse needs to move before next image is shown
let distThreshold = 100;
// scale factor to size images
let scaleFactor = 5;

// VARIABLES
// array to hold all of our images
let images = [];
// array to hold history of mouse positions and image index for that position
let queue = [];
// object containing our last (stored) mouse position
let lastMousePos = { x: 0, y: 0 };
// current image to show
let imgIndex = 0;

// load all of the images from their urls into the images array
function preload() {
  for (let i = 0; i < imageUrls.length; i++) {
    images[i] = loadImage(imageUrls[i]);
  }
}

// setup canvas and store initial mouse position
function setup() {
  let cnv = createCanvas(windowWidth, windowHeight);
  cnv.parent("canvas-parent");
  cnv.style("display", "block");
  cnv.style("position", "absolute");
  cnv.style("inset", "0");
  cnv.style("z-index", "-1");
  lastMousePos = { x: mouseX, y: mouseY };
}

function draw() {
  // clear the canvas
  clear();
  background(0);

  // calculate distance between current mouse position and last stored mouse position.
  let d = dist(mouseX, mouseY, lastMousePos.x, lastMousePos.y);

  // If distance is greater than threshold:
  // 1. Add current mouse position and current image index to the front of the queue
  // 2. Update lastMousePos to current mouse position
  // 3. Update imgIndex to next image index
  if (d > distThreshold) {
    queue.unshift({ x: mouseX, y: mouseY, index: imgIndex });
    lastMousePos = { x: mouseX, y: mouseY };
    imgIndex = (imgIndex + 1) % images.length;
  }

  // maintain queue length equal to number of images by removing the last item
  if (queue.length > images.length) {
    queue.pop();
  }

  // define scale of images based on width of canvas
  let scale = width / scaleFactor;

  // draw images in queue
  // draw order is reversed so that the first image in the queue is drawn on top
  for (let i = queue.length - 1; i >= 0; i--) {
    let img = images[queue[i].index];
    if (img) {
      // scale image based on scale factor
      let imgWidth = (img.width * scale) / img.width;
      let imgHeight = (img.height * scale) / img.width;
      // draw image at mouse position offset by half of image width and height
      image(
        img,
        queue[i].x - imgWidth / 2,
        queue[i].y - imgHeight / 2,
        imgWidth,
        imgHeight
      );
    }
  }
}

// resize canvas when window is resized
function windowResized() {
  resizeCanvas(windowWidth, windowHeight);
}

Breaking It Down

Loading Images: We start by loading images into an array during the preload() function. This ensures all assets are ready before we start rendering.

Setting Up the Canvas: The setup() function initializes our canvas and attaches it to the div in Webflow. Notice the use of absolute positioning to ensure it’s behind everything else on the page—this keeps your interactions unobstructed.

Tracking Mouse Movement: The core of the interaction is in the draw() function. Here, we calculate the distance between the current mouse position and the last recorded position. If the mouse has moved beyond a certain threshold, we add the current position and corresponding image to the front of our queue, creating the trail effect.

Maintaining the Trail: The queue is kept at a manageable length, ensuring that once it’s full, the oldest position is dropped off. This gives the illusion of a fading trail.

Drawing the Images: We loop through our queue to draw each image on the canvas. Images are scaled based on the canvas size, and each one is centered around the mouse’s position.

Handling Resize Events: Finally, the windowResized() function keeps everything responsive by adjusting the canvas size on window resize.

Making It Mobile-Friendly

Here’s the kicker: this effect is mouse-driven, which means it doesn’t translate well to mobile devices. If you’re designing a responsive site, you’ll want to either disable this effect on mobile or replace it with something touch-friendly. You can achieve this by adding a media query or JavaScript logic to detect the screen size and disable the p5.js script when on mobile.

Final Thoughts

Look, JavaScript animations like this aren’t just bells and whistles—they’re a powerful tool for creating a memorable user experience. By carefully crafting these interactions, you make your site stand out in an ocean of static pages. Whether you’re a seasoned dev or just starting to dabble in creative coding, p5.js opens up a world of possibilities. So go ahead, add that flair, and let your site breathe a little more life.

And remember, if this post lit a spark in you, there’s always more to learn. Keep pushing the boundaries, and before you know it, you’ll be creating experiences that users won’t forget.