js记录鼠标动作并按发生时间重现

码途觅雪鹤
• 阅读 2327

场景

demo 演示记录鼠标在一个区域内的动作,并记录下来。点击回放时,按时序回放动作,包括移动,点击,拖拽

源代码

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>鼠标事件记录并回放</title>
    <style>
        * {
            margin: 0;
            padding: 0;
        }

        .container {
            display: flex;
            height: 600px;
        }

        .ctrls {
            display: flex;
        }

        .ctrls div {
            flex: 1 1 auto;
        }

        .p-action,
        .p-repay {
            width: 100%;
            height: 100%;
            position: relative;

            display: flex;
            flex-wrap: wrap;
            flex-direction: row;
        }

        .p-action {
            background: #f0f0f0;
        }

        .p-repay {
            background: #5aab94;
        }

        .mouse-img {
            width: 30px;
            position: absolute;
            left: 0;
            top: 0;
        }

        .btn {
            width: 60px;
            height: 30px;
            line-height: 2em;
            margin: 2em;
            background-color: #eee;
            display: flex;
            justify-content: center;
            align-items: center;
        }

        .btn.hover {
            background-color: #888888;
        }

        .metas {
            position: absolute;
            /* bottom: 40px;
            left: 40px; */
        }

        .metas img {
            width: 40px;
            left: 0px;
            top: 480px;
            position: absolute;
        }

        .metas .meta2 {
            left: 60px;
        }

        .metas .meta3 {
            left: 120px;
        }

        .metas .meta4 {
            left: 180px;
        }

        .metas .meta5 {
            left: 240px;
        }

        .metas .meta6 {
            left: 300px;
        }
    </style>
</head>

<body>

    <div class="ctrls">

        <button class="btn" onclick="start()">开始记录</button>

        <button class="btn" onclick="replay()">开始播放</button>

    </div>

    <div class="container">

        <div class="p-action">
            <button class="btn btn1" onclick="btnClicked(1)">button1</button>
            <button class="btn btn2" onclick="btnClicked(2)">button2</button>
            <button class="btn btn3" onclick="btnClicked(3)">button3</button>
            <button class="btn btn4" onclick="btnClicked(4)">button4</button>

            <div class="metas">
                <img class="meta1" src="./square.png" alt="">
                <img class="meta2" src="./circle.png" alt="">
                <img class="meta3" src="./triangle.png" alt="">
                <img class="meta4" src="./square.png" alt="">
                <img class="meta5" src="./circle.png" alt="">
                <img class="meta6" src="./triangle.png" alt="">
            </div>

            <img class="mouse-img" style="left:0;top:0;display: none;" src="./timg.png" alt="">
        </div>

        <!-- <div class="p-repay">
            <button class="btn btn-r-1" onclick="btnClicked(1)">button1</button>
            <button class="btn btn-r-2" onclick="btnClicked(2)">button2</button>
            <button class="btn btn-r-3" onclick="btnClicked(3)">button3</button>
            <button class="btn btn-r-4" onclick="btnClicked(4)">button4</button>

            <div class="metas">
                <img class="meta-1-r" src="./square.png" alt="">
                <img class="meta-2-r" src="./circle.png" alt="">
                <img class="meta-3-r" src="./triangle.png" alt="">
                <img class="meta-4-r" src="./square.png" alt="">
                <img class="meta-5-r" src="./circle.png" alt="">
                <img class="meta-6-r" src="./triangle.png" alt="">
            </div>

            <img class="mouse-img" style="left:0;top:0;" src="./timg.png" alt="">
        </div> -->
    </div>
</body>
<script>
    const eventL = [];
    const rootEl = document.getElementsByClassName("p-action")[0];
    const rootElTop = rootEl.getBoundingClientRect().y,
        rootElLeft = rootEl.getBoundingClientRect().x;

    const mouseImg = document.getElementsByClassName("mouse-img")[0];
    let startTime;
    let replayLastEvtTime = 0; // 上一个事件的时间(用于计算下一次事件需要等待时间)

    let isMouseDown = false; // 鼠标是否按下去

    //  页面上可操作的元素
    const metaList = [
        {
            className: "meta1",
            points: [[40, 480], [80, 480], [80, 520], [40, 520]]
        },
        {
            className: "meta2",
            points: [[60, 480], [100, 480], [100, 520], [60, 520]]
        },
         {
            className: "meta3",
            points: [[120, 480], [160, 480], [160, 520], [100, 520]]
        },
        {
            className: "meta4",
            points: [[180, 480], [220, 480], [220, 520], [180, 520]]
        },
        {
            className: "meta5",
            points: [[240, 480], [280, 480], [280, 520], [240, 520]]
        },
        {
            className: "meta6",
            points: [[280, 480], [320, 480], [320, 520], [280, 520]]
        },
    ];



    //  鼠标按下事件
    function mouseDown() {
        isMouseDown = true;
    }
    //  记录鼠标轨迹
    function mouseMove(e) {

        const x = e.clientX - rootElLeft,
            y = e.clientY - rootElTop;

        console.log("x,y:", x, y)
        store({
            evtType: isMouseDown ? "drag" : "mousemove",
            data: {
                x: x,
                y: y
            }
        });

        //  执行当前元素拖拽动作
        // if (isMouseDown) {

        //     const meta = getDragMeta([x, y]);
        //     console.log(meta);
        //     if (meta) {
        //         meta.style.left = x + "px";
        //         meta.style.top = y + "px";
        //     }
        // }

    }
    //  鼠标抬起事件
    function mouseUp() {
        isMouseDown = false;
    }

    //  记录按钮点击事件
    function btnClicked(id) {

        store({
            evtType: "click",
            data: {
                metaId: id,
            }
        });
    }

    /***
    * 记录当前事件
    */
    function store(data) {

        //  记录当前事件的时间(等待时间:即开始点击开始记录后,多少毫秒后执行这个动作)
        const time = new Date().getTime();
        eventL.push(Object.assign(data, {
            time: time - startTime
        }));

    }

    //  重现
    function replay() {

        console.log("事件总数:", eventL.length);
        rootEl.removeEventListener("mousemove", mouseMove, false);
        rootEl.removeEventListener("mousemove", mouseMove, false);
        rootEl.removeEventListener("mouseup", mouseUp, false);
        mouseImg.style.display = "inline";
        replayMeta();
    };

    function replayMeta() {

        let currentEvt = eventL.shift();
        if (!currentEvt) {
            replayLastEvtTime = 0;
            console.log("end……")
            return;
        }
        // 等待本次事件的时间到了才执行,完成后下一次事件进入等待 
        awateEvtTime(currentEvt.time - replayLastEvtTime)
            .then(() => {
                switch (currentEvt.evtType) {
                    case "mousemove":
                        console.log("do mousemove");
                        mouseImg.style.left = currentEvt.data.x + "px";
                        mouseImg.style.top = currentEvt.data.y + "px";
                        break;

                    case "drag":
                        const meta = getDragMeta([currentEvt.data.x, currentEvt.data.y]);
                        if (meta) {
                            meta.style.left = currentEvt.data.x + "px";
                            meta.style.top = currentEvt.data.y + "px";
                        }

                        //  拖拽同时鼠标也要跟着移动
                        mouseImg.style.left = currentEvt.data.x + "px";
                        mouseImg.style.top = currentEvt.data.y + "px";

                        break;

                    case "click":
                        console.log("do click");
                        let btn = document.getElementsByClassName("btn" + currentEvt.data.metaId)[0];
                        btn.classList.add("hover");

                        setTimeout(() => {
                            btn.classList.remove("hover");
                        }, 800);
                        break;
                }
                replayLastEvtTime = currentEvt.time;
                replayMeta();
            });

    }

    // 本次事件的需要等待的时间
    function awateEvtTime(timeout) {

        return new Promise((resolve, reject) => {
            setTimeout(() => {
                resolve();
            }, timeout);

        })
    }

    function start() {
        rootEl.addEventListener("mousedown", mouseDown, false);
        rootEl.addEventListener("mousemove", mouseMove, false);
        rootEl.addEventListener("mouseup", mouseUp, false);

        mouseImg.style.display = "none";
        startTime = new Date().getTime();
    }

    // 得到拖动的对象
    function getDragMeta(point) {
        for (let i = 0; i < metaList.length; i++) {
            let meta = metaList[i];
            if (pointInPolygon(point, meta.points)) {
                return document.getElementsByClassName(meta.className)[0];
            }
        }
    }

    /**
     * 判断点是否在另一平面图中
     * {Array} point [x,y] 这个点
     * {Array} vs [[x,y],[x,y]]平面点集合 
     */
    function pointInPolygon(point, vs) {

        console.log(point, vs);
        const x = point[0], y = point[1];

        let inside = false;
        for (let i = 0, j = vs.length - 1; i < vs.length; j = i++) {
            const xi = vs[i][0], yi = vs[i][1];
            const xj = vs[j][0], yj = vs[j][1];

            const intersect = ((yi > y) !== (yj > y))
                && (x < (xj - xi) * (y - yi) / (yj - yi) + xi);
            if (intersect) {
                inside = !inside;
            }
        }
        console.log(inside);
        return inside;
    }

</script>

</html>

效果图

没有gif -_-!!!

js记录鼠标动作并按发生时间重现

可能存在的问题

  1. 未测试最大可记录事件数量
  2. 如果一次事件未在1ms内完成,进入下个动作会怎么样
点赞
收藏
评论区
推荐文章
blmius blmius
4年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
Wesley13 Wesley13
3年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
美凌格栋栋酱 美凌格栋栋酱
7个月前
Oracle 分组与拼接字符串同时使用
SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(
Karen110 Karen110
4年前
一篇文章带你了解JavaScript日期
日期对象允许您使用日期(年、月、日、小时、分钟、秒和毫秒)。一、JavaScript的日期格式一个JavaScript日期可以写为一个字符串:ThuFeb02201909:59:51GMT0800(中国标准时间)或者是一个数字:1486000791164写数字的日期,指定的毫秒数自1970年1月1日00:00:00到现在。1\.显示日期使用
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
Wesley13 Wesley13
3年前
mysql设置时区
mysql设置时区mysql\_query("SETtime\_zone'8:00'")ordie('时区设置失败,请联系管理员!');中国在东8区所以加8方法二:selectcount(user\_id)asdevice,CONVERT\_TZ(FROM\_UNIXTIME(reg\_time),'08:00','0
Wesley13 Wesley13
3年前
Java日期时间API系列36
  十二时辰,古代劳动人民把一昼夜划分成十二个时段,每一个时段叫一个时辰。二十四小时和十二时辰对照表:时辰时间24时制子时深夜11:00凌晨01:0023:0001:00丑时上午01:00上午03:0001:0003:00寅时上午03:00上午0
Wesley13 Wesley13
3年前
00:Java简单了解
浅谈Java之概述Java是SUN(StanfordUniversityNetwork),斯坦福大学网络公司)1995年推出的一门高级编程语言。Java是一种面向Internet的编程语言。随着Java技术在web方面的不断成熟,已经成为Web应用程序的首选开发语言。Java是简单易学,完全面向对象,安全可靠,与平台无关的编程语言。
Stella981 Stella981
3年前
Django中Admin中的一些参数配置
设置在列表中显示的字段,id为django模型默认的主键list_display('id','name','sex','profession','email','qq','phone','status','create_time')设置在列表可编辑字段list_editable
Stella981 Stella981
3年前
Docker 部署SpringBoot项目不香吗?
  公众号改版后文章乱序推荐,希望你可以点击上方“Java进阶架构师”,点击右上角,将我们设为★“星标”!这样才不会错过每日进阶架构文章呀。  !(http://dingyue.ws.126.net/2020/0920/b00fbfc7j00qgy5xy002kd200qo00hsg00it00cj.jpg)  2
Python进阶者 Python进阶者
1年前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这