svg VS canvas,哪种在移动端适配度更好?实战经历告诉你~

最近做了一个画图的功能,后端提供棋盘数据,前端需要把数据转换成一个棋盘画出来,当时有两种实现方法,一种是使用canvas画图,一个是使用svg画图.

ui提供的设计稿如下:

在这里插入图片描述

由于这是一个轮播图,当后端返回多少张棋盘数据时,就需要渲染多少张棋盘,最多有十张,基于canvas和svg画图,为了保证页面的首次渲染时长最优,就是不使用库来画,正好可以熟悉一下canvas和svg的基础.

svg画棋盘

适配

由于是在移动端的页面,所以首选就是使用rem单位,但是画着画着就出现了问题

svg画简单形状的代码是

<p>使用rem单位画线</p>
  <svg>
    <line
      x1="0"
      y1="0"
      x2="5rem"
      y2="5rem"
      stroke-width="2"
      stroke="#975B2A"
    />
  </svg>
  <p>使用rem单位画圆</p>
  <svg>
    <circle 
      cx="6rem" 
      cy="6rem" 
      fill="#A7632D" 
      r="0.3rem"
    ></circle>
  </svg>

效果如下:

在这里插入图片描述

在电脑上能够正常显示,但是到iphone13上,就显示不出来了.

我发现是小于1的rem单位的显示不出来,不过我没有使用各种各样的机型测试,但是iphone13就不行,那就只能放弃了.

所以就只能依赖js计算

40px的线条距离:40 * document.documentElement.clientWidth / 375;

虽然不能使用rem单位,但是可以使用百分比,使用svg的DOM元素使用rem单位还是没有问题的,使用百分比就能够正常显示

<p>使用百分比单位画线</p>
  <svg>
    <line
      x1="0"
      y1="0"
      x2="50%"
      y2="50%"
      stroke-width="2"
      stroke="#975B2A"
    />
  </svg>
  <p>使用百分比单位画圆</p>
  <svg>
    <circle 
      cx="60%" 
      cy="60%" 
      fill="#A7632D" 
      r="1%"
    ></circle>
  </svg>

在这里插入图片描述

不过使用js计算一次就可以了,百分比对着ui稿我还得计算一遍,所以我就直接使用document.documentElement.clientWidth计算了.

很快棋盘的样式就画好了

在这里插入图片描述

所以很快就被ui设计师打了回来,没有光泽,她说棋子需要画渐变,也需要有阴影,不然不好看.

加渐变

<p>加渐变(first:没渐变,second:有渐变)</p>
  <svg>
    <defs>
      <linearGradient id="b" x1="0" x2="1" y1="0" y2="1">
          <stop offset="0" stop-color="#fff" stop-opacity="1"></stop>
          <stop offset="1" stop-color="#D0CBC7" stop-opacity="1"></stop>
      </linearGradient>
  </defs>
  <circle 
      cx="20%" 
      cy="60%" 
      fill="#fff" 
      r="3rem"
    ></circle>
    <circle 
      cx="60%" 
      cy="60%" 
      fill="url(#b)" 
      r="3rem"
    ></circle>
  </svg>
  <svg>
    <defs>
      <linearGradient id="a" x1="0" x2="1" y1="0" y2="1">
          <stop offset="0" stop-color="#7B7B7B" stop-opacity="1"></stop>
          <stop offset="1" stop-color="#0B0B0B" stop-opacity="1"></stop>
      </linearGradient>
  </defs>
  <circle 
      cx="20%" 
      cy="60%" 
      fill="#000" 
      r="3rem"
    ></circle>
    <circle 
      cx="60%" 
      cy="60%" 
      fill="url(#a)" 
      r="3rem"
    ></circle>
  </svg>

效果对比如下:
在这里插入图片描述

渐变兼容性在PC,iphone,ipad,安卓都支持~

加阴影

<p>加阴影(first:没阴影,second:有阴影)</p>
  <svg>
    <defs>
      <linearGradient id="b" x1="0" x2="1" y1="0" y2="1">
          <stop offset="0" stop-color="#fff" stop-opacity="1"></stop>
          <stop offset="1" stop-color="#D0CBC7" stop-opacity="1"></stop>
      </linearGradient>
      
      <filter id="bb" width="200%" height="200%">
        <!-- 投影偏移 -->
        <feOffset dx="0" dy="2" />
        <!-- 投影模糊 -->
        <feGaussianBlur stdDeviation="2" result="offset-blur" />
        <!-- 反转投影使其变成内投影 -->
        <feComposite
          operator="out"
          in="SoredurceGraphic"
          in2="offset-blur"
          result="inverse"
        />
        <!-- 内投影附加黑色 -->
        <feFlood flood-color="#BE8C2E" flood-opacity=".95" result="color" />
        <feComposite operator="in" in="color" in2="inverse" result="shadow" />
        <!-- 把内投影显示在图像上 -->
        <feComposite operator="over" in="shadow" in2="SourceGraphic" />
      </filter>
  </defs>
  <circle 
      cx="20%" 
      cy="60%" 
      fill="url(#b)" 
      r="3rem"
    ></circle>
    <circle 
      cx="60%" 
      cy="60%" 
      fill="url(#b)"
      filter="url(#bb)"
      r="3rem"
    ></circle>
  </svg>
  <svg>
    <defs>
      <linearGradient id="a" x1="0" x2="1" y1="0" y2="1">
          <stop offset="0" stop-color="#7B7B7B" stop-opacity="1"></stop>
          <stop offset="1" stop-color="#0B0B0B" stop-opacity="1"></stop>
      </linearGradient>
      <filter id="aa" x="-0.05" y="0" width="120%" height="120%">
        <!-- 投影偏移 -->
        <feOffset dx="0" dy="1" />
        <!-- 投影模糊 -->
        <feGaussianBlur stdDeviation="1" result="offset-blur" />
        <!-- 反转投影使其变成内投影 -->
        <feComposite
          operator="out"
          in="SoredurceGraphic"
          in2="offset-blur"
          result="inverse"
        />
        <!-- 内投影附加黑色 -->
        <feFlood flood-color="#1D1D1D" flood-opacity=".95" result="color" />
        <feComposite operator="in" in="color" in2="inverse" result="shadow" />
        <!-- 把内投影显示在图像上 -->
        <feComposite operator="over" in="shadow" in2="SourceGraphic" />
      </filter>
  </defs>
  <circle 
      cx="20%" 
      cy="60%" 
      fill="url(#a)" 
      r="3rem"
    ></circle>
    <circle 
      cx="60%" 
      cy="60%" 
      fill="url(#a)" 
      filter="url(#aa)"
      r="3rem"
    ></circle>
  </svg>

效果如下:

在这里插入图片描述

阴影没有加渐变这么顺利,出现了新的问题

iphone中棋子不见了

在这里插入图片描述

排查原因:发现去掉去掉filter标签就能够看到棋子.

于是我查看svg中filter标签的兼容性。如下:

https://caniuse.com/?search=filter svg

在这里插入图片描述

上面写着IOS6就开始支持了,但是在iphone13中就是没有显示出来,所以就只能不能这样画阴影了,得另寻他路.

重叠元素加阴影

  <p>重叠元素加阴影(first:没阴影,second:有阴影)</p>
  <svg>
    <defs>
      <linearGradient id="b" x1="0" x2="1" y1="0" y2="1">
          <stop offset="0" stop-color="#fff" stop-opacity="1"></stop>
          <stop offset="1" stop-color="#D0CBC7" stop-opacity="1"></stop>
      </linearGradient>
  </defs>
  <circle 
      cx="20%" 
      cy="60%" 
      fill="url(#b)" 
      r="3rem"
    ></circle>
    <circle 
      cx="60%" 
      cy="64%" 
      fill="rgba(0,0,0,.16)"
      r="3rem"
    ></circle>
    <circle 
      cx="60%" 
      cy="60%" 
      fill="url(#b)"
      r="3rem"
    ></circle>
    
  </svg>
  <svg>
    <defs>
      <linearGradient id="a" x1="0" x2="1" y1="0" y2="1">
          <stop offset="0" stop-color="#7B7B7B" stop-opacity="1"></stop>
          <stop offset="1" stop-color="#0B0B0B" stop-opacity="1"></stop>
      </linearGradient>
  </defs>
  <circle 
    cx="60%" 
    cy="64%" 
    fill="rgba(0,0,0,.24)"
    r="3rem"
  ></circle>
  <circle 
      cx="20%" 
      cy="60%" 
      fill="url(#a)" 
      r="3rem"
    ></circle>
    <circle 
      cx="60%" 
      cy="60%" 
      fill="url(#a)" 
      r="3rem"
    ></circle>

  </svg>

效果如下:

在这里插入图片描述

不过这个需要特别注意顺序,阴影需要在下面显示,所以要先绘制阴影,不然就会把棋子盖住,效果像下面这样:

在这里插入图片描述

突然发现使用重叠元素加出来的阴影更加好看.

边缘棋子不显示问题

在这里插入图片描述

svg中的cricle标签没有z-index层级之说

解决办法:svg面积增大,svg中的元素x,y轴偏移,根据rect画圆角矩形

<p>画圆角矩形</p>
<svg>
    <rect 
    width="60%" 
    height="60%" 
    x="22" 
    y="22" 
    rx="20" 
    ry="20" 
    style="
      fill: transparent; 
      stroke: rgb(151, 91, 42); 
      fill-opacity: 0.5; 
      stroke-opacity: 1; 
      opacity: 1;
      stroke-width: 2;
    "
    ></rect>
  </svg>
  // x,y是矩形的起点;w,h是矩形的宽高;r是圆角矩形的半径

效果如下:
在这里插入图片描述

canvas和svg比较

android效果

canvas截图如下:

在这里插入图片描述

svg截图如下:

在这里插入图片描述

ipad效果

canvas截图如下:

在这里插入图片描述

svg截图如下:

在这里插入图片描述

canvas拍照效果:

在这里插入图片描述

svg拍照效果:

在这里插入图片描述

iphone效果

canvas截图如下:

在这里插入图片描述

svg截图如下:

在这里插入图片描述

canvas拍照效果:

在这里插入图片描述

svg拍照效果:

在这里插入图片描述

从上面效果来说,我觉得svg画的棋盘在移动端更高清,而canvas更像是一张图片,对效果有更加浓厚的兴趣的可以去下载一下源码,然后自己手动在手机上看看效果.

性能比较

我只是通过谷歌浏览器的lighthouse测量了一下,下面是绘制十张棋盘的测试结果,当然觉得这样测量不准确的,也欢迎下载源码自己去测试一下~

canvas

在这里插入图片描述

svg

在这里插入图片描述

从上面结果上看,svg和canvas画十张棋盘的性能,svg优胜一点点,总阻塞时间svg快了100ms的样子.

源码:

为了保证公平性,源码中没有使用任何库,只是js操作,十张棋盘的数据也是一样的.

gitee地址:svg_vs_canvas_chess: svg和canvas在移动端适配比较 (gitee.com)

github地址:https://github.com/tiantianhy/svg_vs_canvas_chess

上述分享中,如有疑问和想法,欢迎一起探讨~

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值