美文网首页
“集智知识星空”产品技术解剖(一)

“集智知识星空”产品技术解剖(一)

作者: 如意同学Try | 来源:发表于2019-03-23 16:11 被阅读0次

    在上一篇文章开创史无前例的在线学习方式——“集智知识星空”产品技术解剖(一)中我们讲了产品新版本的特点,简单来说就是三点:

    1. 使用二维展示方式,展示的信息更多维,更丰富。
    2. 使用层级化展示,每个层级有对应的信息重点,在展示更多信息的同时,不产生视觉负担。
    3. 高手可便捷地自行探索学习路径,同时也为初学者提供了推荐的学习路径。

    那既然作为一个程序员,从本篇文章开始就要剖析产品中用到的技术了。整个产品前后端交互不多,核心在于后端算法生成数据,和前端酷炫的交互实现两部分。

    算法过程还涉及到机密啊专利啊等等乱七八糟的事情,不能说的太详细,但前端部分本身就完全对外公开,所以也谈不上技术保护。所以我们会着重对前端的实现部分进行分享和分析。

    还没有体验过的同学,可以前往集智学园官网体验后再继续往下看。

    模拟地图功能

    所有的课程以分布在二维坐标系上的点的形式呈现。那就有对视图在二维平面中上下左右移动的需求。而且为了展示内部细节,还需要支持缩放。本质上就是一个地图。所以我们首先需要实现地图的基本交互,移动 + 缩放

    之所以不使用google或者百度地图这类现有的地图框架,一是因为我们其实只需要地图的部分交互,其实没必要引入庞大的地图库;二是我们希望能更灵活地对这个"地图"进行自定义开发,后续可能会在现有基础上增加更多的交互或者元素。

    另外地图组件本质是图片的分片加载,所以难免在移动和缩放的时候出现中间加载时刻。所以在经过了一段时间的尝试之后我们放弃了对地图库的引入。

    1. 核心绘图

    整个视图的组成主要元素是那些课程点,这些点都是绘制在一个canvas上
    核心绘图函数很简单

    drawPoint (point) {
      ctx.arc(point.x, point.y, point.r, 0, 2 * Math.PI);
    }
    

    点位的坐标生成是另外的技术话题,大致流程是将课程信息(包括资料,文本,标签等)提取出来转化为高维课程特征矩阵,再通过聚类和降维技术映射成二维坐标。具体实现将另开篇幅。本文针对前端实现方式,不对此展开讨论。

    2. 引入监听事件
    1. 移动功能用到了
    • mousedown, // 鼠标移动
    • mousestart // 鼠标点下
    • mouseup // 鼠标抬起
    1. 缩放功能用到了
    • dblclick // 鼠标双击
    • mousewheel // 鼠标滚轮
    • DOMMouseScroll // firfox的鼠标滚轮
      设置事件函数,将所有事件绑定在视图的canvas上
    //设置事件
        setHandler(dom) {
          //鼠标双击
          dom.addEventListener( 'dblclick',e => {
              onDocumenDblClick(e, this, false);
            }, { passive: true });
          //鼠标按下
          dom.addEventListener('mousedown', e => {
              moveDown(e, this, false);
            }, { passive: true });
          //鼠标移动
          dom.addEventListener('mousemove', e => {
            moveMouse(e, this, point);
          });
          //鼠标抬起
          dom.addEventListener( 'mouseup', e => {
             moveUP(e, this);
            }, { passive: true });
          //鼠标滚轮
          dom.onmousewheel = e => { e.stopPropagation();
            mouseScroll(e, this, false);
          };
          // 鼠标滚轮事件firfox
          dom.addEventListener('DOMMouseScroll', e => {
            mouseScroll(e, this, false);
          });
        },
    

    设置好事件后,就是地图功能实现的核心了。移动 + 缩放

    3. 拖拽移动功能

    移动主要监听mousemove事件,这就需要对单纯的“鼠标移动”,和按下后的“拖拽”做一个区分,所以需要mousedownmouseup事件的配合,来判断当前是否为拖拽状态。

    let dragFlag = false; // 拖拽标识
       /*鼠标点下事件   @param {*} e event */
      moveDown (e) => {
        dragFlag = true; // 鼠标被按下,准备拖拽
      }
      /*鼠标抬起事件   @param {*} e event */
      moveUP (e) => {
        dragFlag = false; //结束拖拽标识
      },
      /** 拖拽事件  @param {*} e event */
      moveMouse (e) => {
        if (dragFlag) {
          ...
          transform(x, y);  // x, y为地图移动的距离
        }
      },
    
    

    至于拖拽的距离,则取决于上一时刻的位置,和当前位置的差值。所以在移动的过程中,需要去记录上一时刻的位置。初始位置,为鼠标按下的位置

    let lastPointPos = [];
    // 鼠标按下
     moveDown (e) => {
        dragFlag = true; // 鼠标被按下,准备拖拽
        lastPointPos = [e.clientX, e.clientY]
      }
    // 鼠标拖拽
     moveMouse (e) => {
       if (dragFlag) {
         let x = e.clientX - lastPoint[0];
         let y = e.clientY - lastPoint[1];
         lastPoint = [e.clientX, e.clientY];
         transform(x, y);
      }
    }
    

    这样一来, transform函数就能专注实现移动点位

    //  移动点位函数
    transform (x, y) => {
        this.x = this.x + x;
        this.y = this.y + y
        drawPoint();
      })
    }
    

    到这里,拖拽移动地图的功能基本完成

    接下去,我们来说一说稍微复杂的缩放操作。

    4. 缩放功能

    有很多操作会触发缩放:

    1. 双击地图
    2. 鼠标滚动
    3. 笔记本触控板

    双击触发dbclick事件
    鼠标滚动和触控板的行为基本一致,都是触发鼠标滚轮mousewheel(firfox触发的是DOMMouseScroll事件)

    // 双击事件
    onDocumenDblClick (e) => {
     ...
      let flag = 'large';
      scale(x, y, flag)  // scale为缩放函数,传入缩放中心,和放大还是缩小标志
    }
    // 滚动事件
    mouseScroll (e) => {
      ...
      scale(x, y, flag)  // scale为缩放函数,传入缩放中心,和放大还是缩小标志
    }
    

    因为每次双击的缩放尺度,和每次滚轮的缩放尺度,显然是不一样的。所以两个行为的缩放倍数。肯定不一样。我们可以设置,每触发一次双击事件,就相当于触发了n次的scale(n为一个自定义的参数), 即

    onDocumenDblClick (e) => {
      ...
      let flag = 'large';
      let count = 0;
      let time = setInterval(() => {
      if (count <= n) {
          scale(x, y, flag)  // scale为缩放函数,传入缩放中心,和放大还是缩小标志
        } else {
          clearInterval(time)  
        }
      }, 100)
    }
    

    这么写当然可以实现功能,但是一点都不优雅,而且使用setInterval做动画对浏览器来说并不是一个最佳的渲染方案,点位多的时候容易有失帧现象。这里钻一下细节,使用requestAnimationFrame改写下。

    let scaleStartTime = 0; // 开始放大的起始时间
    // 双击事件
    onDocumenDblClick (e) => {
      ...
      let flag = 'large';
      scaleStartTime = performance.now();
      scaleOnceAnimation(e,  time,  flag);  //  time是自定义参数,自行设置动画要运行的时间。
    }
    // 循环动画
    scaleOnceAnimation (e, time, flag) => {
       // 使用当前时间和起始时间做对比,每次循环都判断是否已经达到设置的动画运行时间。
       if (performance.now() - scaleStartTime > time) {
         scaleStartTime = 0;
          return;
        }
        scale(x,  y,  flag);
        window.requestAnimationFrame(() => {
          scaleOnceAnimation(e, time, flag);
        });
    }
    

    最后就是scale函数的实现。在直接写代码之前,我们先来做个简单的数学题。

    以p(1, 1)为中心,把圆(2, 2, r = 1)放大为原来的两倍,求圆放大后的坐标和半径

    初始状态

    第一步,移动整个坐标,直至p位于(0, 0)点,此时圆坐标为(1, 1, r = 1)


    1

    第二步,放大整个坐标系至相应倍数,这里为2倍, 得到圆(2, 2, r = 2)


    2

    第三步,把坐标系移回原来的位置,让p回到初始点,得到圆(3, 3, r = 2)


    3

    从这道题中可以看出,要把一个点以某一中心进行缩放,还需要借助平移的方法,所以讲了这么一堆,可以得出缩放函数应该这么写

    // 缩放函数
    scale (x, y, flag) => {
      let scale = flag === 'large' ? 110 / 100 ? 100 / 110; // 缩放比例
      transform(-x, -y);
      this.x = this.x * scale;
      this.y = this.y * scale;
      transform(x, y);
      this.drawPoint()
      })
    }
    

    到此为止,缩放的功能就也已经基本实现。一个模拟地图行为的产品也已经实现了最核心的功能。

    在此基础上,我们还可以模拟其他衍伸功能,比如:

    • viewPort (pointArray):把传入的点放置于视图中合适的位置;
    • panTo (x, y):把视图移动到某个位置,并以传入的坐标为视图中心(或任何一个你想要的位置点)
    • openWindow (point) :打开点位的信息窗口
      除了模拟地图API的基本功能以外,还能根据需求开发自己的地图新功能
    • scaleToValue(point, value):对某个点移动到视图中心,并放大到指定大小
    • scaleToRange(range) :缩放地图,直到满足传入到视图范围内
      ....

    由于是完全canvas手撸的地图,所以完全可以根据需求开发想要的功能,虽然可能一开始如果选择了地图框架来实现功能,前期进展肯定会比现在快,但到了后期开发,我相信一定是我们自己的框架更加灵活,更有利于实现我们的想法,而不会被技术所局限。

    本篇主要介绍了地图的基础操作移动缩放是如何实现的。
    在下一篇,我们来介绍一下更加精彩的“窗口”和“路径”实现。
    敬请期待。

    20190322090155-da5a5d4806.[gif-2-mp4.com].gif

    相关文章

      网友评论

          本文标题:“集智知识星空”产品技术解剖(一)

          本文链接:https://www.haomeiwen.com/subject/eevjkqtx.html