• 【GISER&&Painter】svg的那些事


    一 什么是SVG

    SVG(可缩放矢量图形),基于XML定义的二维图形描述文档。它拥有矢量特征:1)在缩放图像尺寸的过程中,图形质量高保真;2)保持交互性(可编辑/可选/可搜索)3)符合开放标准;
     

    二 SVG和Canvas该选哪个?

    2-1 特性优劣分析

    下面的内容来自于https://docs.microsoft.com/en-us/previous-versions/windows/internet-explorer/ie-developer/samples/gg193983(v=vs.85)关于H5中svg和canvas的使用场景选择说明:
     
    【主观因素】
     
      z1)开发人员技术栈,如果开发人员对于图形学了解更多,而并非一个纯粹的web端开发,则有可能会倾向于选择canvas
     
      z2)性能要求:从下图中可以看出,在屏幕范围扩大的变化下,SVG的性能比较稳定,和屏幕尺寸的相关度不强。所以在大屏幕处理非大批量数据时,可以考虑svg技术。但如果时在渲染要素量比较大的时候,就应当考虑canvas技术
     
    【客观因素】
     
      k1)高保真的复杂矢量文档
      根据介绍,在Web端进行展示高保真的复杂矢量文档图形的时候,都需要保证在放大或者缩小数据时细节详情无损。一般对于建筑工程图纸/电子航空图纸/组织结构图/地图/生物结构图等场景来说,svg是我们的不二之选。
     
      k2)静态图形细节优势
      由于svg本身的保真特性,可以用svg格式替代png格式,作为高清图像加载在Web端。
     
      k3)像素级别的图像处理
      如果对于图像有细节处理的需求,例如需要创建复杂的视觉效果,例如三维场景建设/光照特效等。在遇到这些需求的时候,我们需要逐像素处理,这个时候我们会更倾向于选择canvas。
     
      k4)数据的实时更新:在绘制大量的实时数据刷新渲染的场景下,我们应当考虑采用canvas作为渲染基础,因为将svg的渲染要素到DOM,并实现对应的场景效果的开销是远远大于canvas的离屏渲染的。我们利用canvas的成图效果,可以实现大量实时场景的构建和变化,类似于动画片的原理,而非直接操作DOM元素。
     
      k5)图表:由于图表本身有以下几个特点:
        a 图表数据是根据现有数据生成的;
        b 用户需要和图表进行互动;
        c 图表的样式是需要修改的;
     
      综合以上三点,所以图表一般采用SVG创建。

    2-2 图表和地图场景下的选择

    【图表】
      著名的图表组件库Echart中提供了两种渲染方式,SVG和Canvas,可以根据不同的需求进行渲染选择,总体上来说,开发者需要根据软硬件环境、数据量以及功能场景需求。具体可见:https://blog.csdn.net/WuLex/article/details/78828321
     
    【地图】
      写到这里,我就想起了基于webgl的三维地图渲染原理,其中包括了mapbox和cesium这些知名的地图渲染引擎都是选择了canvas画布作为渲染核心,从而去实现复杂的地图需求。这与前面我们提到的高保真SVG构建地图矛盾吗?
      当然不矛盾,我还能举出几个以SVG作为渲染技术实现的地图应用或者API,比如OpenLayers早期版本,osm的id编辑器等等,这些都是采用SVG作为渲染底层实现。
      那么问题来了,为什么会有这些区别呢?
      显而易见,这些选择完全取决于我们的业务场景:
        1)对于mapbox/cesium这些三维渲染引擎而言,需要渲染大量的数据构建三维场景,其中场景计算庞大,需要高性能的计算方式(从上文的性能图片就能看出,canvas更适合);
        2)对于早期的Open Layers、osm的id编辑器来说,这些API的需求更侧重于前端某些矢量元素的编辑,而非整体场景的展现。而SVG适用于侧重交互/数据量一般的编辑场景。
      综上所述,场景需求是决定我们技术方案选型的重要依据。
     

    三 SVG的使用实例

    3-1 在html中使用svg

      1)svg外部文件【复杂图形】
      引入方式:embed/object/iframe
     
      (a) embed(仅适用于h5,可使用脚本):
      <embed src="circle1.svg" type="image/svg+xml" />
     
      (b) object(普适,但不可使用脚本):
      <object data="circle1.svg" type="image/svg+xml"></object>
     
      (c) iframe(仅适用于h5,可使用脚本):
      <iframe src="circle1.svg"></iframe>
     
      2)自定义svg基础矢量图形<svg>标签
      矩形/原型/椭圆/直线/多边形/曲线/路径/文本
      <svg xmlns="http://www.w3.org/2000/svg" version="1.1">
          <rect width="300" height="100" style="fill:rgb(0,0,255);stroke-1;stroke:rgb(0,0,0)"/>
      </svg>

    3-2  和svg交互操作实现

    下图是一个SVG文件实例,从line3可以看到,svg标签中有一个固定属性viewBox,这是我们操作svg变换的主要属性。
     
     
     
    我们可以把viewBox理解成为一个相机,我们通过相机镜头去观察图形,如果我们想要让镜头里显示图形rect的全景,我们需要设置镜头和图形之间的距离(假定镜头定焦),这样才能保证镜头视角里能够装下完整的图形rect。
    viewBox=“x, y, width, height”,图示如下:
     
     
    从图中我们能够很容易理解,viewBox其实类似于一个截图窗口,当我们定义的截图窗口截取了部分数据之后,就会将部分数据填充到整个svg范围内;同理,如果viewBox要大于svg元素尺寸时,可以在viewBox中展示img效果。
    具体实现代码如下:
    // func:获取当前svg的viewBox
    function getCurrentVB () {
        let theSvgElement = theSvgObj.getSVGDocument().documentElement;
        vbCX = parseFloat(theSvgElement.viewBox.animVal.x);
        vbCY = parseFloat(theSvgElement.viewBox.animVal.y);
        vbCW = parseFloat(theSvgElement.viewBox.animVal.width);
        vbCH = parseFloat(theSvgElement.viewBox.animVal.height);
    }
    
    // func:平移svg
    function svgMouseDown(evt){
        getCurrentVB(theSvgObj);
        panFlag = true;
        svgStartX = parseInt(evt.clientX);
        svgStartY = parseInt(evt.clientY);
    }
    
    function svgMouseMove(evt){
    
        if(panFlag){
            let theSvgElement = theSvgObj.getSVGDocument().documentElement;
            theSvgElement.setAttributeNS(null, 'style', 'cursor: move')
            svgMoveX = parseInt(evt.clientX) - svgStartX;
            svgMoveY = parseInt(evt.clientY) - svgStartY;
            vbCX = svgEndX - svgMoveX;
            vbCY = svgEndX - svgMoveY;
            theSvgElement.setAttributeNS(null, 'viewBox',vbCX + ' ' + vbCY + ' ' + vbCW + ' ' + vbCH);
        }
    
    }
    
    function svgMouseUp(evt){
        svgEndX = vbCX;
        svgEndY = vbCY;
        panFlag = false;
    }
    
    // func:缩放svg
    function scrollSvgZoom(e){
        // 兼容火狐detail
        let down = e.wheelDelta?e.wheelDelta < 0:e.detail > 0;
        if(down){
            zoomOut();
        }else{
            zoomIn();
        }
    
    }
    
    function zoomIn(){
        if(zoomW > zoomStepSize * 2 && zoomH > zoomStepSize * 2 ){
            zoomVal += zoomStepSize;
            zoomTo('in');
        }
    }
    
    function zoomOut(){
        zoomVal -= zoomStepSize;
        if(zoomVal >= -zoomStepSize*11){
            zoomTo('out')
        }else{
            zoomVal = -zoomStepSize * 11;
        }
    
    }
    
    function zoomTo(flag){
        // 获取最新的viewBox
        getCurrentVB();
    
        if(flag === 'in'){
            zoomX = vbCX + zoomStepSize;
            zoomY = vbCY + zoomStepSize;
            zoomW = vbCW - zoomStepSize * 2;
            zoomH = vbCH - zoomStepSize * 2;
        }else{
            zoomX = vbCX - zoomStepSize;
            zoomY = vbCY - zoomStepSize;
            zoomW = vbCW + zoomStepSize * 2;
            zoomH = vbCH + zoomStepSize * 2;
        }
    
        let theSvgDocument = document.getElementById('svgPic').getSVGDocument();
        let theSvgElement = theSvgDocument.documentElement;
    
        theSvgElement.setAttributeNS(null, 'viewBox', zoomX+' '+zoomY+' '+zoomW+' '+zoomH);
        endZoom();
    }
    
    function endZoom(){
        svgEndX = vbCX;
        svgEndY = vbCY;
    }
     

  • 相关阅读:
    [转载自 文顶顶]iOS开发UI篇—程序启动原理和UIApplication
    中专生自学Android到找到工作的前前后后
    HashCode与IEqualityComparer接口
    abp vnext使用记录之微服务
    asp.net core 外部认证多站点模式实现
    xamarin.android 绑定百度地图SDK遇到的问题
    asp.net core 之多语言国际化自定义资源文件
    asp.net core tags 扩展之 id 和 name
    asp.net 后台任务作业框架收集
    向量
  • 原文地址:https://www.cnblogs.com/escage/p/14368329.html
Copyright © 2020-2023  润新知