<div class="image"></div>
<script src="jquery.js"></script>
<script src="cursor-trails.js"></script>
<script>
$('.image').blurredImage({
image: 'image-normal.jpg',
blur: 'image-with-blur.jpg',
lifetime: 1000, // time the trail remains on screen
width: 1000, // image width
height: 1000, // image height
minimumLineWidth: 25, // trail minimum width
maximumLineWidth: 100, // trail maximum width
maximumSpeed: 50, // at which speed should the trail get thinner (lower = more thinner trails)
shadowBlur: 30 // blur of the trails edges
});
</script>
Last active
September 28, 2019 22:47
-
-
Save r15ch13/38efc2410558e6b1c815314dc60ab162 to your computer and use it in GitHub Desktop.
cursor trails on a blurred image
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| /* globals jQuery */ | |
| /** | |
| * Based on https://codepen.io/chrisdoble/pen/WQLLVp | |
| */ | |
| (function ($) { | |
| window.requestAnimationFrame = window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame | |
| $.fn.blurredImage = function (options) { | |
| if (!this.length > 0) { | |
| return false | |
| } | |
| // This is the easiest way to have default options. | |
| let settings = $.extend({ | |
| image: '', | |
| blur: '', | |
| lifetime: 1000, | |
| width: 1280, | |
| height: 720, | |
| minimumLineWidth: 25, | |
| maximumLineWidth: 100, | |
| maximumSpeed: 50, | |
| shadowBlur: 30 | |
| }, options) | |
| let points = [] | |
| let image = $('<img src="' + settings.image + '">').hide() | |
| let imageCanvas = $('<canvas>') | |
| let imageCanvasContext = imageCanvas[0].getContext('2d') | |
| let lineCanvas = $('<canvas>') | |
| let lineCanvasContext = lineCanvas[0].getContext('2d') | |
| this.append(image) | |
| this.append(imageCanvas) | |
| this.css('background', 'url(\'' + settings.blur + '\') 0 0 / cover') | |
| if (image[0].complete) { | |
| start() | |
| } else { | |
| image[0].onload = start | |
| } | |
| /** | |
| * Attaches event listeners and starts the effect. | |
| */ | |
| function start () { | |
| image.width(settings.width) | |
| image.height(settings.height) | |
| $(this).parent('div').width(settings.width) | |
| $(this).parent('div').height(settings.height) | |
| $(this).parent('div')[0].addEventListener('mousemove', function (event) { | |
| points.push({ | |
| time: Date.now(), | |
| x: event.offsetX, | |
| y: event.offsetY | |
| }) | |
| }) | |
| window.addEventListener('resize', function () { | |
| imageCanvas[0].width = $(this).parent('div').width() | |
| imageCanvas[0].height = $(this).parent('div').height() | |
| lineCanvas[0].width = $(this).parent('div').width() | |
| lineCanvas[0].height = $(this).parent('div').height() | |
| }.bind(this)) | |
| window.dispatchEvent(new window.Event('resize')) | |
| tick() | |
| } | |
| /** | |
| * The main loop, called at ~60hz. | |
| */ | |
| function tick () { | |
| // Remove old points | |
| points = points.filter(function (point) { | |
| return (Date.now() - point.time) < settings.lifetime | |
| }) | |
| drawLineCanvas() | |
| drawImageCanvas() | |
| window.requestAnimationFrame(tick) | |
| } | |
| /** | |
| * Draws a line using the recorded cursor positions. | |
| * | |
| * This line is used to mask the original image. | |
| */ | |
| function drawLineCanvas () { | |
| let minimumLineWidth = settings.minimumLineWidth | |
| let maximumLineWidth = settings.maximumLineWidth | |
| let lineWidthRange = maximumLineWidth - minimumLineWidth | |
| let maximumSpeed = settings.maximumSpeed | |
| lineCanvasContext.clearRect(0, 0, lineCanvas[0].width, lineCanvas[0].height) | |
| lineCanvasContext.lineCap = 'round' | |
| lineCanvasContext.shadowBlur = 30 | |
| lineCanvasContext.shadowColor = '#000' | |
| for (let i = 1; i < points.length; i++) { | |
| let point = points[i] | |
| let previousPoint = points[i - 1] | |
| // Change line width based on speed | |
| let distance = getDistanceBetween(point, previousPoint) | |
| let speed = Math.max(0, Math.min(maximumSpeed, distance)) | |
| let percentageLineWidth = (maximumSpeed - speed) / maximumSpeed | |
| lineCanvasContext.lineWidth = minimumLineWidth + percentageLineWidth * lineWidthRange | |
| // Fade points as they age | |
| let age = Date.now() - point.time | |
| let opacity = (settings.lifetime - age) / settings.lifetime | |
| lineCanvasContext.strokeStyle = 'rgba(0, 0, 0, ' + opacity + ')' | |
| lineCanvasContext.beginPath() | |
| lineCanvasContext.moveTo(previousPoint.x, previousPoint.y) | |
| lineCanvasContext.lineTo(point.x, point.y) | |
| lineCanvasContext.stroke() | |
| } | |
| } | |
| /** | |
| * @param {{x: number, y: number}} a | |
| * @param {{x: number, y: number}} b | |
| * @return {number} The distance between points a and b | |
| */ | |
| function getDistanceBetween (a, b) { | |
| return Math.sqrt(Math.pow(a.x - b.x, 2) + Math.pow(a.y - b.y, 2)) | |
| } | |
| /** | |
| * Draws the original image, masked by the line drawn in drawLineToCanvas. | |
| */ | |
| function drawImageCanvas () { | |
| // Emulate background-size: cover | |
| let width = imageCanvas[0].width | |
| let height = imageCanvas[0].width / image[0].naturalWidth * image[0].naturalHeight | |
| if (height < imageCanvas[0].height) { | |
| width = imageCanvas[0].height / image[0].naturalHeight * image[0].naturalWidth | |
| height = imageCanvas[0].height | |
| } | |
| imageCanvasContext.clearRect(0, 0, imageCanvas[0].width, imageCanvas[0].height) | |
| imageCanvasContext.globalCompositeOperation = 'source-over' | |
| imageCanvasContext.drawImage(image[0], 0, 0, width, height) | |
| imageCanvasContext.globalCompositeOperation = 'destination-in' | |
| imageCanvasContext.drawImage(lineCanvas[0], 0, 0) | |
| } | |
| } | |
| }(jQuery)) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment