图形基础篇01 # 浏览器中实现可视化的四种方式

Posted 凯小默

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了图形基础篇01 # 浏览器中实现可视化的四种方式相关的知识,希望对你有一定的参考价值。

说明

【跟月影学可视化】学习笔记。

方式一:html+CSS

优点:方便,不需要第三方依赖,甚至不需要 javascript 代码。

缺点:CSS 属性不能直观体现数据,绘制起来也相对麻烦,图形复杂会导致 HTML 元素多,而消耗性能。

方式二:SVG

SVG (Scalable Vector Graphics,可缩放矢量图)是一种基于 XML 语法的图像格式,可以用图片(img 元素)的 src 属性加载。SVG 是对 HTML/CSS 的增强,弥补了 HTML 绘制不规则图形的能力。

优点:简单,直观,方便

缺点:图形复杂时需要的 SVG 元素太多,也非常消耗性能。

方式三:Canvas2D

HTML/CSS 还是 SVG,它们都属于声明式绘图系统,也就是我们根据数据创建各种不同的图形元素(或者 CSS 规则),然后利用浏览器渲染引擎解析它们并渲染出来。Canvas 调用绘图指令,然后引擎直接在页面上绘制图形。这是一种指令式的绘图系统

优点:高性能绘制

缺点:直接操作图形元素不方便,如果要绘制的图形太多,或者处理大量的像素计算时,Canvas2D 依然会遇到性能瓶颈。

Canvas 能够直接操作绘图上下文,不需要经过 HTML、CSS 解析、构建渲染树、布局等一系列操作。因此单纯绘图的话,Canvas 比 HTML/CSS 和 SVG 要快得多。

另外可以使用 SVG 生成某些图形,然后用 Canvas 来渲染。这样,我们就既可以享受 SVG 的便利性,又可以享受 Canvas 的高性能。

现在浏览器的 canvas 一般有 webgl2、webgl 和 2d 三种上下文。它们并不是一个完整的 canvas api 规范,而是分成了 2d 规范和 webgl 规范。webgl 规范是 opengl es 规范在 web 端的实现,其中 webgl2 对应 opengl es 3.0,而 webgl 对应的是 opengl es 2.0。

方式四:WebGL

优点:功能强大、更适合绘制 3D 场景、大批量绘制、超高性能

缺点:使用繁琐,难度相对较高

使用 WebGL 的情景:

  1. 要绘制的图形数量非常多:比如有多达数万个几何图形需要绘制
  2. 对较大图像的细节做像素处理:比如,实现物体的光影、流体效果和一些复杂的像素滤镜,需要处理的像素点数量非常的多(一般是数十万甚至上百万数量级的)。
  3. 绘制 3D 物体:WebGL 内置了对 3D 物体的投影、深度检测等特性,不需要对坐标做底层的处理。

图形系统与浏览器渲染引擎工作对比

相比于 HTML 和 CSS,Canvas2D 和 WebGL 更适合去做可视化这一领域的绘图工作。它们的绘图 API 能够直接操作绘图上下文,一般不涉及引擎的其他部分,在重绘图像时,也不会发生重新解析文档和构建结构的过程,开销要小很多。

技术选型

几个不错的问题

Canvas 是不是有5M的大小限制?

Canvas画布大小有限制,不同的浏览器不同,一般的可视化大屏足够用了。检测设备的Canvas大小可以用这个项目:https://github.com/jhildenbiddle/canvas-size

Desktop


Mobile

canvas2d绘制出来的图形最终也是渲染到gpu中的吧,和webgl渲染到底区别在哪里,为啥webgl性能好

因为 canvas2d 渲染只能由浏览器底层控制,并不能自己控制gpu,而很多优化其实浏览器并不能代替开发者去做。比如说同时绘制几万个小圆形,因为图形都一样,自己写 webgl 的话,可以用 instanced drawing 的方式批量绘制,而 canvas2d 写浏览器不会帮你去这么做。可以说 webgl 在渲染大量元素的时候手段要更多得多,所以性能差别就明显了。

canvas2d 绘图是通过自身的 api,gpu 是浏览器底层调用的,不受开发者控制。webgl 不一样,将数据写入帧缓冲之后,最终通过WebGLProgram 来执行 shader 完成图形渲染,所以 webgl 能够自己控制 gpu 渲染。有很多图形计算,webgl 是可以放在 shader 里面去计算的,这样比用 js 计算快,这就是 gpu 和 cpu 计算的区别。

Canvas绘出圆形颜色渐变的倒计时图形有种朦胧感,怎么回事

这个牵扯到设备像素比dpr了。我们知道 mac 和 iphone 的 dpr 是 2,也就是说一个如果你在这样的设备上绘制 canvas,应当将它的画布坐标设置为样式坐标的 2 倍,才可以清晰地显示图像。浏览器的 window.devicePixelRatio 属性可以读取设备像素比。

实例:不同方式实现饼图

请参考:https://codepen.io/gltjk/pen/vYLmdvJ

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

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>kiamo pie</title>
    <style>
        .pie-graph 
            display: inline-block;
            width: 250px;
            height: 250px;
            border-radius: 50%;
            background-image: conic-gradient(
                #37c 30deg, 
                #3c7 30deg,
                #3c7 65deg,
                orange 65deg,
                orange 110deg,
                #f73 110deg,
                #f73 200deg,
                #ccc 200deg
            )
        
    </style>
</head>

<body>
    <h1>HTML+CSS、SVG、Canvas 三种方式实现饼图</h1>
    <table>
        <tr>
            <th>HTML+CSS</th>
            <th>SVG</th>
            <th>Canvas</th>
        </tr>
        <tr>
            <td>
                <div class="pie-graph"></div>
            </td>
            <td>
                <div id="svg"></div>
            </td>
            <td>
                <div id="canvas"></div>
            </td>
        </tr>
    </table>
    <script>
        // 数据处理
        function prepare( values, colors ) 
            const sum = values.reduce((x, y) => x + y)
            return values.map((x, i) => [(x / sum) * 2 * Math.PI, colors[i]])
        

        // svg 绘制饼图
        function drawSvgPie(el,  data, center, radius ) 
            const paths = []
            let start =  x: center.x, y: center.y - radius 
            let deg = 0
            for (const [value, color] of data) 
                deg += value
                const end = 
                    x: center.x + radius * Math.sin(deg),
                    y: center.y - radius * Math.cos(deg)
                
                const largeArc = value >= Math.PI ? 1 : 0
                const pathD =
                    `M $center.x $center.y` +
                    `L $start.x $start.y` +
                    `A $radius $radius 0 $largeArc 1 $end.x $end.y` +
                    'Z'
                paths.push(`<path d="$pathD" fill="$color" />`)
                start = end
            
            const d = radius * 2
            el.innerHTML =
                '<svg xmlns="http://www.w3.org/2000/svg" ' +
                    `width="$dpx" height="$dpx" viewBox="0 0 $d $d">` +
                    paths.join('') +
                '</svg>'
        

        // canvas 绘制饼图
        function drawCanvasPie(el,  data, center, radius ) 
            const canvas = document.createElement('canvas')
            canvas.setAttribute('width', radius * 2)
            canvas.setAttribute('height', radius * 2)
            const ctx = canvas.getContext('2d')
            let start = -Math.PI / 2
            for (const [value, color] of data) 
                const end = start + value
                ctx.beginPath()
                ctx.arc(center.x, center.y, radius, start, end, false)
                ctx.lineTo(center.x, center.y)
                ctx.fillStyle = color
                ctx.fill()
                ctx.closePath()
                start = end
            
            el.append(canvas)
        

        // 页面加载完成
        window.onload = function () 
            const values = [30, 35, 45, 90, 160]
            const colors = ['#37c', '#3c7', 'orange', '#f73', '#ccc']
            const commonData = 
                data: prepare( values, colors ),
                center:  x: 125, y: 125 ,
                radius: 125
            
            console.log(commonData)
            drawCanvasPie(document.querySelector('#canvas'), commonData)
            drawSvgPie(document.querySelector('#svg'), commonData)
        
    </script>
</body>

</html>

以上是关于图形基础篇01 # 浏览器中实现可视化的四种方式的主要内容,如果未能解决你的问题,请参考以下文章

跟月影学可视化学习笔记 41 篇(完结)

跟月影学可视化学习笔记 41 篇(完结)

JAVA中实现多线程的四种方式

设置 matplotlib 正确显示中文的四种方式 看这一篇就够啦!

设置 matplotlib 正确显示中文的四种方式 看这一篇就够啦!

基础总结篇之二:Activity的四种launchMode