使用Javascript编写一个鼠标交互式跟随特效和光标交互效果

17,796次阅读
没有评论

共计 5936 个字符,预计需要花费 15 分钟才能阅读完成。

编写一个互动(并且超级令人满意)的光标:7 个简单的步骤 + 2KB 的代码

近期我制作了这个光标动画,人们似乎很喜欢它 :)

这是一个很好看的作品,但同时也非常简单,只需 2KB 的 JS 代码。

而且,这种方法非常通用,可以作为其他美丽作品的模板使用。因此,它值得有一个逐步指南!

鼠标光标特效结果

步骤 #1:设置

我们正在 元素上绘图,并且需要 全屏显示。

canvas {
    position: fixed;
    top: 0;
    left: 0;
}
setupCanvas();
window.addEventListener("resize", setupCanvas);

function setupCanvas() {
    canvas.width = window.innerWidth;
    canvas.height = window.innerHeight;
}

当然,我们需要跟踪光标位置。

const pointer = {
    x: .5 * window.innerWidth,
    y: .5 * window.innerHeight,
}

window.addEventListener("click", e => {updateMousePosition(e.clientX, e.clientY);
});
window.addEventListener("mousemove", e => {updateMousePosition(e.clientX, e.clientY);
});
window.addEventListener("touchmove", e => {updateMousePosition(e.targetTouches[0].clientX, e.targetTouches[0].clientY);
});

function updateMousePosition(eX, eY) {
    pointer.x = eX;
    pointer.y = eY;
}

步骤 #2:动画循环

要看到最简单的鼠标跟随动画,我们只需要使用该方法循环重画画布window.requestAnimationFrame(),并在每一步绘制以指针坐标为中心的圆。

const p = {x: 0, y: 0}; // coordinate to draw

update(0);

function update(t) {ctx.clearRect(0, 0, canvas.width, canvas.height);

    // copy cursor position
    p.x = poiner.x;
    p.y = poiner.y;
    // draw a dot
    ctx.beginPath();
    ctx.arc(p.x, p.y, 5, 0, 2 * Math.PI);
    ctx.fill();

    window.requestAnimationFrame(update);
}

通过上面的代码,我们有一个跟随鼠标的黑色圆圈。

鼠标跟随

步骤 #3:添加延迟

现在,圆圈会尽可能快地跟随光标。让我们添加一个延迟,以便点以某种弹性方式赶上目标位置。

const params = {
    // ...
    spring: .4
};
// p.x = poiner.x;
// p.y = poiner.y;
p.x += (pointer.x - p.x) * params.spring;
p.y += (pointer.y - p.y) * params.spring;

ctx.beginPath();
ctx.arc(p.x, p.y, 5, 0, 2 * Math.PI);
ctx.fill();

spring 参数用于确定点追上光标位置的速度。像 0.1 这样的小值会使其跟随非常慢,而 spring = 1 意味着没有延迟。

鼠标延迟跟随

步骤 #3:创建鼠标轨迹

让我们创建一个点数据的轨迹数组,每个点都保存我们用来计算延迟的 x / y 坐标和 dx/ 增量。dy

const params = {
    // ...
    pointsNumber: 30
};

// const p = {x: 0, y: 0};

const trail = new Array(params.pointsNumber);
for (let i = 0; i 

我们现在绘制整个轨迹,而不是单个点,其中每个点都试图赶上前一个点。第一个点追上了光标坐标 (pointer),并且第一个点的延迟更长 - 只是因为它对我来说看起来更好:)

function update(t) {ctx.clearRect(0, 0, canvas.width, canvas.height);

    trail.forEach((p, pIdx) => {const prev = pIdx === 0 ? pointer : trail[pIdx - 1];
        const spring = pIdx === 0 ? .4 * params.spring : params.spring;

        p.dx = (prev.x - p.x) * spring;
        p.dy = (prev.y - p.y) * spring;

        p.x += p.dx;
        p.y += p.dy;

        ctx.beginPath();
        ctx.arc(p.x, p.y, 5, 0, 2 * Math.PI);
        ctx.fill();});

    window.requestAnimationFrame(update);
}

鼠标踪迹效果

步骤 #4:将点转向线

绘制折线而不是点很容易。

trail.forEach((p, pIdx) =/> {const prev = pIdx === 0 ? pointer : trail[pIdx - 1];

    p.dx = (prev.x - p.x) * params.spring;
    p.dy = (prev.y - p.y) * params.spring;

    p.x += p.dx;
    p.y += p.dy;

    // ctx.beginPath();
    // ctx.arc(p.x, p.y, 5, 0, 2 * Math.PI);
    // ctx.fill();

    if (pIdx === 0) {
        // start the line on the first point
        ctx.beginPath();
        ctx.moveTo(p.x, p.y);
    } else {
        // continue with new line segment to the following one
        ctx.lineTo(p.x, p.y);
    }
});

// draw the thing
ctx.stroke();

鼠标折线

步骤 #5:累积速度

使光标动画看起来非常漂亮的是累积增量。让我们不仅使用 dx/dy 来表示到邻居位置的距离,而且还累加这个距离。

为了防止增量值变得超级大超级快,我们还在每个步骤上将 dx/dy 与新参数相乘。friction

const params = {
    // ...
    friction: .5
};

...

// ...

// p.dx = (prev.x - p.x) * spring;
// p.dy = (prev.y - p.y) * spring;
p.dx += (prev.x - p.x) * spring;
p.dy += (prev.y - p.y) * spring;
p.dx *= params.friction;
p.dy *= params.friction;

// as before
p.x += p.dx;
p.y += p.dy;

// ...

鼠标积累效果

步骤 #6:平滑线条

动议完成!让我们让笔画看起来更好,并将每条线段替换为贝塞尔曲线。

trail.forEach((p, pIdx) =/> {
    // calc p.x and p.y

    if (pIdx === 0) {ctx.beginPath();
        ctx.moveTo(p.x, p.y);
    // } else {//     ctx.lineTo(p.x, p.y);
    }
});

for (let i = 1; i 

光滑的!

使用 Javascript 编写一个鼠标交互式跟随特效和光标交互效果

步骤#7:调整线宽

对于此演示,最后一步是将默认值 lineWidth1px 替换为每个段变得更小的动态值。

const params = {baseWidth: .9,};
...

for (let i = 1; i 

使用 Javascript 编写一个鼠标交互式跟随特效和光标交互效果

全源码示例

HTML




CSS

body, html {
    padding: 0;
    margin: 0;
     overscroll-behavior: none;
}

/* for tutorial link only */
.links {
    position: fixed;
    bottom: 10px;
    right: 10px;
    font-size: 18px;
    font-family: sans-serif;
     background-color: white;
     padding: 10px;
}
a {
    text-decoration: none;
    color: black;
    margin-left: 1em;
}
a:hover {text-decoration: underline;}
a img.icon {
    display: inline-block;
    height: 1em;
    margin: 0 0 -0.1em 0.3em;
}

JS

const canvas = document.querySelector("canvas");
const ctx = canvas.getContext('2d');

// for intro motion
let mouseMoved = false;

const pointer = {
    x: .5 * window.innerWidth,
    y: .5 * window.innerHeight,
}
const params = {
    pointsNumber: 40,
    widthFactor: .3,
    mouseThreshold: .6,
    spring: .4,
    friction: .5
};

const trail = new Array(params.pointsNumber);
for (let i = 0; i  {updateMousePosition(e.pageX, e.pageY);
});
window.addEventListener("mousemove", e => {
    mouseMoved = true;
    updateMousePosition(e.pageX, e.pageY);
});
window.addEventListener("touchmove", e => {
    mouseMoved = true;
    updateMousePosition(e.targetTouches[0].pageX, e.targetTouches[0].pageY);
});

function updateMousePosition(eX, eY) {
    pointer.x = eX;
    pointer.y = eY;
}

setupCanvas();
update(0);
window.addEventListener("resize", setupCanvas);


function update(t) {

    // for intro motion
    if (!mouseMoved) {pointer.x = (.5 + .3 * Math.cos(.002 * t) * (Math.sin(.005 * t))) * window.innerWidth;
        pointer.y = (.5 + .2 * (Math.cos(.005 * t)) + .1 * Math.cos(.01 * t)) * window.innerHeight;
    }

    ctx.clearRect(0, 0, canvas.width, canvas.height);
    trail.forEach((p, pIdx) => {const prev = pIdx === 0 ? pointer : trail[pIdx - 1];
        const spring = pIdx === 0 ? .4 * params.spring : params.spring;
        p.dx += (prev.x - p.x) * spring;
        p.dy += (prev.y - p.y) * spring;
        p.dx *= params.friction;
        p.dy *= params.friction;
        p.x += p.dx;
        p.y += p.dy;
    });

    ctx.beginPath();
    ctx.moveTo(trail[0].x, trail[0].y);

    for (let i = 1; i 


文章来源地址 https://www.toymoban.com/diary/js/395.html

到此这篇关于使用 Javascript 编写一个鼠标交互式跟随特效和光标交互效果的文章就介绍到这了, 更多相关内容可以在右上角搜索或继续浏览下面的相关文章,希望大家以后多多支持 TOY 模板网!

    正文完
     0
    Yojack
    版权声明:本篇文章由 Yojack 于1970-01-01发表,共计5936字。
    转载说明:
    1 本网站名称:优杰开发笔记
    2 本站永久网址:https://yojack.cn
    3 本网站的文章部分内容可能来源于网络,仅供大家学习与参考,如有侵权,请联系站长进行删除处理。
    4 本站一切资源不代表本站立场,并不代表本站赞同其观点和对其真实性负责。
    5 本站所有内容均可转载及分享, 但请注明出处
    6 我们始终尊重原创作者的版权,所有文章在发布时,均尽可能注明出处与作者。
    7 站长邮箱:laylwenl@gmail.com
    评论(没有评论)