使用html2canvas截图踩坑总结

       年底的移动端H5需求中,再次用到了html2canvas这个插件,这个插件主要是用来对网页进行截图,在项目需求中,有个交互的点,就是通过用户操作,将页面的内容截图保存下来,方便用户传播扩散。

H5说明:

        H5的大致交互:用户进入页面,首先loading加载页面图片资源,加载完成后,显示相关图片元素,随后进入下个页面,用户可以通过点击按钮截图当前内容,并且分享出去,最后一页则是用户的个性标签页,同样是可以截图分享出去的

        html2canvas版本1.4.1

做这个需求里面,碰到的问题点:

      1、用户第一次进入页面,一整套流程下来没有问题,包括截图等操作,但是问题来了,再次进入页面后,会发现页面卡在加载图片资源那边,通过手机连接电脑,打开网页检查器发现控制台有报错,报错内容如下:

       按照预期的效果是不会出现跨域问题的,因为已经在obs配置好了跨域资源共享 

       2、使用html2canvas测试截图的过程中发现,截图后保存的图片文件里面,一些特定的元素底部边缘,会有颜色断层,看着很突兀,实际在页面上查看是不会出现这个问题的

底部有颜色断层
底部有颜色断层

这里要说明一下,这些元素的背景是使用了渐变背景色,比如这样

        3、在 ios 下使用 html2canvas 截图时,会触发页面全部资源的重新加载包括音频资源,这就导致了截图后,ios 会重复播放背景音乐,导致听起来会有多条音轨同时播放的感觉

 如何解决问题:

       1. 跨域问题:

       这个问题困扰了我几天,明明我在华为云obs已经设置了跨域选项,也就是常用的

Access-Control-Allow-Origin:*

在电脑上代码程序跑起来正常,不会出现跨域错误的提示,然而,在后面是ios机子上面发现第一次打开是不会出现问题的,第二次就会,一开始是以为这个问题是偶然触发,多次测试(清缓存等),才的出来规律。但是这个问题以前没碰到过,然后再掘金的一篇文章(跨域漏洞,我把前端线上搞崩溃了)上看到这句话

这句话一下子就点亮了我的思路,我们来模拟下明明配置了跨域资源共享 ,第二次打开页面还是会出现跨域问题。

       前言:本复现页面上有一个audio标签,图片加载进度条,使用js(new Image方法)加载图片资源,并且资源加载成功后,会通过img标签显示其中一张图片,代码如下:

<template>
  <div>
    <p>进度条</p>
    <div class="box">
      <div class="inner" :style="{ width: `${state.current}%` }"></div>
    </div>
    <div class="text-box">
      <p>测试图片测试图片</p>
      <div class="image-wrap">
        <!-- <img v-if="state.current === 100" :src="state.imageUrl" alt="" /> -->
        <img :src="state.imageUrl" alt="" />
      </div>
      <p>图片地址: {{ state.imageUrl }}</p>
    </div>
  </div>
</template>
<script setup>
import imageLoader from './someJs/imageLoader'
import imageUrls from './someJs/imageFiles'
import { reactive, onMounted } from 'vue'

const state = reactive({
  current: 0,
  imageUrl:
    'https://mobile-assets.obs.cn-south-1.myhuaweicloud.com/image/auto/driving-report/images/scence/pic_together_z03.png'
})

onMounted(() => {
  imageLoader({
    imageUrls,
    onProgress: (val) => {
      state.current = val
    }
  })
})
</script>
<style scoped lang="scss">
.box {
  width: 400px;
  height: 20px;
  border-radius: 10px;
  border: 1px solid #888;
  box-sizing: border-box;
  overflow: hidden;
  .inner {
    background: #888;
    height: 100%;
  }
}

.text-box {
  background-color: aqua;
  width: 400px;
  margin-top: 100px;
  .image-wrap {
    width: 300px;
    height: 300px;
    overflow: hidden;
    & > img {
      width: 100%;
      height: 100%;
    }
  }
}
</style>

       首先我们打开控制台,在 Network 选项卡里面,将 Disabled cache 打钩,模拟没有缓存第一次进入,如下图

 我们观察控制台

能够看到,第一次打开页面,一切正常,能够按照预期展示页面内容,js进度条也能够正常加载图片资源。此时我们再将 Disabled cache 的打钩给取消掉,再次刷新页面

我们可以看到,再次刷新页面后,出现了跨域的报错,在 Network 里面,出现跨域的地方是发起图片资源请求的 imageLoader 报错了,进度条没有加载完就可以看到已经出现了报错了。

分析以下第一次加载页面的过程:

       1、先使用 js 加载图片资源

       2、加载成功后,再显示图片

       注意这个顺序是先 js,再通过 img 标签来直接显示图片。使用 js 发起的请求,在这种条件(图片在obs桶)下,是属于跨域请求的,故头部会带上 Origin 这个属性(头部 Origin 属性的介绍),如果是同源,则不需要带上这个头部。

js请求图片资源
img标签可以直接获取并显示图片,并不受同源策略的影响

       可以看到,js 加载完成后,img标签又去再获取一次同一个图片资源,那么问题来了,第一次加载页面是能够按照预期执行成功的,但是第二次 js 加载同一个文件就出现了跨域错误。我通过多次复现这个情况,猜测是因为后面(img标签)获取同一个图片资源,将前面缓存的图片信息给覆盖掉,缓存里面没有 cross-origin-access-control 这个字段,导致 js 第二次get请求获取的时候直接触发同源策略,代码报跨域错误。

       要验证以上猜测,很简单,只需要调整图片的加载顺序即可,我们只需要在代码里面,将img的显示时机改为直接显示,也就是优先于 js 的请求,代码修改如下(省略了css)

<template>
  <div>
    <p>进度条</p>
    <div class="box">
      <div class="inner" :style="{ width: `${state.current}%` }"></div>
    </div>
    <div class="text-box">
      <p>测试图片测试图片</p>
      <div class="image-wrap">
        <!-- 当加载完成才显示img标签 -->
        <!-- <img v-if="state.current === 100" :src="state.imageUrl" alt="" /> -->
        <!-- 直接显示 -->
        <img :src="state.imageUrl" alt="" />
      </div>
      <p>图片地址: {{ state.imageUrl }}</p>
    </div>
  </div>
</template>
<script setup>
import imageLoader from './someJs/imageLoader'
import imageUrls from './someJs/imageFiles'
import { reactive, onMounted } from 'vue'

const state = reactive({
  current: 0,
  imageUrl:
    'https://xxxxx.com/pic_together_z03.png'
})

onMounted(() => {
  // dom挂载后再js加载
  imageLoader({
    imageUrls,
    onProgress: (val) => {
      state.current = val
    }
  })
})
</script>
<style scoped lang="scss">

</style>

       现在代码的执行就是组件挂载后,img标签先发起拉取图片的请求,不携带 Origin 头部,随后页面挂载回调,执行 js 请求图片资源,

先img标签,再js获取图片

这种情况下,当我们把控制台的 Disabled Cache 关闭后,再次刷新页面,js获取的图片资源,会发现是来自缓存的,并且正确携带有 cors 相关头部

       至此,问题一解决,出现这种问题也是由于自身对http头部的不熟悉引起的,如果了解更深入一些,或许可以减少调试时间

       2. 截图渐变色彩背景出现断层问题

       这个问题解决起来比较简单,一开始以为是图片保存质量问题,修改了几次导出图片质量,发现还是会有,排除图片质量问题。在排除问题的过程中,我写了个简单的demo来复现问题,发现并不会出现

<template>
  <div>
    <div class="box" ref="target"></div>
    <button @click="handlePrtScClick">截图</button>
    <div class="result">
      截图结果
      <img :src="state.base64" alt="" />
    </div>
  </div>
</template>
<script setup>
import html2canvas from 'html2canvas'
import { ref, reactive } from 'vue'

const target = ref()

const state = reactive({
  base64: ''
})

const handlePrtScClick = () => {
  html2canvas(target.value, {
    scale: 2,
    useCORS: true
  }).then((canvas) => {
    state.base64 = canvas.toDataURL('image/jpeg', 0.8) // 保存图片质量为0.8
  })
}
</script>
<style lang="scss" scoped>
.box {
  width: 300px;
  height: 200px;
  background-image: linear-gradient(180deg, #7aeee7 0%, #e3f4c7 100%);
}
.result {
  width: 300px;
  & > img {
    width: 100%;
  }
}
</style>

代码的运行结果如下图,并未出现截图色彩断层问题

      demo没问题,但是我们demo的样式是使用px去写的,在项目中,我使用的rem布局,用rem布局写一个试试,代码修改如下

<template>
  <div>
    <div class="box" ref="target"></div>
    <button @click="handlePrtScClick">截图</button>
    <div class="result">
      截图结果
      <img :src="state.base64" alt="" />
    </div>
  </div>
</template>
<script setup>
import html2canvas from 'html2canvas'
import { ref, reactive } from 'vue'

const target = ref()

const state = reactive({
  base64: ''
})

const handlePrtScClick = () => {
  html2canvas(target.value, {
    scale: 2,
    useCORS: true
  }).then((canvas) => {
    state.base64 = canvas.toDataURL('image/jpeg', 0.8) // 保存图片质量为0.8
  })
}
</script>
<style lang="scss" scoped>
.box {
  width: 16rem;
  height: 9.3rem;
  background-image: linear-gradient(180deg, #7aeee7 0%, #e3f4c7 100%);
}
.result {
  width: 16rem;
  & > img {
    width: 100%;
  }
}
</style>

运行后截图,发现结果图片最下方有明显的颜色断层,通过查看css发现,被截图元素的高度,出现了小数,然后我们的渐变方向又是从上而下的,所以导致了截图出现了颜色断层现象。通过查找html2canvas 的源代码发现,在处理渐变色彩的时候,在使用了createPattern 重复渐变背景,然后html2canvas 对画布的宽高计算方式,是向下取整的,这就导致了画布和渲染渐变的地方有误差,误差部分就被repeat出来了,导致最上方的色彩跑到最下方多出来的像素区域

       3. 同时播放多个背景音乐问题:

       这个问题只有在 ios 上才会出现,安卓和 pc 端 chrome 都正常,一开始是认为 ios 的问题,和同时多次调试后发现,当点击截图后,在控制台的 Network 选项卡里面看到多出来了一个音频资源的请求,ios 就会自动播放这个资源,安卓则不会。在写这个文章的时候,我自己调试,发现页面是有一闪而过的 iframe,后来看文档发现,有个参数,如下图:

 大概意思是是否移除被临时克隆出来的 dom 元素,默认是克隆完成后自动移除,在代码里面将此参数 removeContainer 置为 false 后,发现 html2canvas 将页面整体克隆出来,当有存在audio 标签(preload=“auto”)的时候,ios就会自动播放。

       解决这个问题就需要用到 ignoreElements 这个参数:
 

当 element 是你的目标元素时,返回 true 则可以忽略元素。或者还有另外一个办法,我在源码里面找到了 IGNORE_ATTRIBUTE 这个变量,它的值是 data-html2canvas-ignore ,当在标签上配置这个值,则可以忽略当前标签元素的克隆,就可以解决我们碰到的这个问题。

部分源码

结语:

        至此,坑点的解决思路已经完成,可以说是在写的时候没细致阅读文档,也有一些是基础知识不过关,导致花费了挺多时间去排查问题。但还存在着其他问题点未搞定,比如用 URL.createObjectURL 创建出来的临时 url,有些莫名其妙访问不到,这个问题我现在还没有找到原因,待后续摸索。

       博文写得比较口水化,请大家多多指教。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/373045.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【初读论文】

这里写目录标题 万字长文解析深度学习中的术语面向小白的深度学习论文术语&#xff08;持续更新&#xff09;deepsolo不懂的知识pipelinebaselineRoI(Region of Interest)分类问题中的正例负例指示函数&#xff08;indicator function&#xff09;模型性能评估指标&#xff08;…

nginx+flask+Gunicorn反代理服务拿不到真实IP的解决

背景 本人在宝塔linux环境,要部署flask的简单后端并且用Ngnix反代理,用Gunicorn框架部署。(o(╥﹏╥)o中间磕磕绊绊总算部署上去了,需要了解Gunicorn怎么部署的朋友,评论区留言,我加补一篇介绍)。但是但是,我发现 其 accesslog日志里竟然是 127.0.0.1。这怎么能…

模拟钉钉官网动画

实现思路&#xff1a;利用粘性定位sticky&#xff0c;以及滚动事件实现。首先我们应该设置滚动动画开始位置和结束位置 &#xff0c;然后根据位置计算透明度或者transform&#xff0c;scale的值。 首先根据上述图线计算属性值&#xff0c;代码如下&#xff1a; function creat…

Python基础知识:Python模块

所谓模块(Module)&#xff0c;就是一种以“.py”为命名后缀的Python 文件&#xff0c;里面包含着很多集成的函数&#xff0c;可以很方便的被其他程序和脚本导入并使用。 如果模块理解为一辆汽车&#xff0c;我们使用汽车可以完成驾驶等工作&#xff0c;那么代码就是一个个细小…

Linux内存管理:(十二)Linux 5.0内核新增的反碎片优化

文章说明&#xff1a; Linux内核版本&#xff1a;5.0 架构&#xff1a;ARM64 参考资料及图片来源&#xff1a;《奔跑吧Linux内核》 Linux 5.0内核源码注释仓库地址&#xff1a; zhangzihengya/LinuxSourceCode_v5.0_study (github.com) 外碎片化发生时&#xff0c;页面分配…

day21网页布局

文章目录 块元素和行内元素列表标签表格标签媒体元素页面结构分析iframe内联框架 块元素和行内元素 块元素&#xff1a;无论内容多少&#xff0c;该元素独占一行。(p标签、h1~h6…) 行内元素&#xff1a;内容撑开宽度&#xff0c;左右都是行内元素的可以排在一行。&#xff08…

Java基础---IO知识以及常用的API整理

Java 中的 I/O&#xff08;输入/输出&#xff09;是一个核心概念&#xff0c;涉及数据的读取和写入。Java I/O API 提供了丰富的类和接口&#xff0c;用于处理不同类型的数据流。了解 Java I/O 的基础知识对于面试和日常编程都非常重要。所以今天来整理一下&#xff1a; 1. Fi…

day42_jdbc

今日内容 0 复习昨日 1 JDBC概述 2 JDBC开发步骤 3 完成增删改操作 4 ResultSet 5 登录案例 0 复习昨日 1 写出JQuery,通过获得id获得dom,并给input输入框赋值的语句 $(“#id”).val(“值”) 2 mysql内连接和外连接的区别 内连接只会保留完全符合关联条件的数据 外连接会保留表…

LeetCode、746. 使用最小花费爬楼梯【简单,动态规划 线性DP】

文章目录 前言LeetCode、746. 使用最小花费爬楼梯【简单&#xff0c;动态规划 线性DP】题目与分类思路 资料获取 前言 博主介绍&#xff1a;✌目前全网粉丝2W&#xff0c;csdn博客专家、Java领域优质创作者&#xff0c;博客之星、阿里云平台优质作者、专注于Java后端技术领域。…

idea 快捷键ctrl+shift+f失效的解决方案

文章目录 搜狗输入法快捷键冲突微软输入法快捷键冲突 idea的快捷键ctrlshiftf按了没反应&#xff0c;理论上是快捷键冲突了&#xff0c;检查搜狗输入法和微软输入法快捷键。 搜狗输入法快捷键冲突 不需要简繁切换的快捷键&#xff0c;可以关闭它&#xff0c;或修改快捷键。 微…

Linux Shell编程系列--开篇

一、目的 从本篇开始介绍Linux Shell脚本编程&#xff0c;为简单起见&#xff0c;本篇中以一个显示当前时间的shell脚本来帮助大家理解shell脚本的组成。 SHELL脚本中可以包含变量、函数、命令等部分。 二、介绍 我们通过vim新建一个myshell.sh的脚本&#xff0c;然后输入以下…

工作与生活平衡:在生活中寻找和谐

工作和生活是我们生活中不断交织的两个重要方面。对许多人来说&#xff0c;找到两者之间的完美平衡已经成为一个持久的挑战。然而&#xff0c;与其专注于平衡&#xff0c;更重要的是要认识到工作和生活并不是可以相互平衡的两个分离实体&#xff0c;而是一个相互影响的循环。正…

C++之程序内存分配方式

程序内存布局 现在的应用程序都运行在一个虚拟内存空间里&#xff0c;以32位系统为例&#xff0c;其寻址空间为 4G&#xff0c;大部分的操作系统都将4G内存空间的一部分挪给内核调用&#xff0c;应用程序无法直接 访问这一段内存&#xff0c;这一部分内核地址成为内核态空间&am…

LeetCode:13.罗马数字转整数

13. 罗马数字转整数 - 力扣&#xff08;LeetCode&#xff09; 目录 思路&#xff1a; 官解代码&#xff1a; 作者辣眼代码: 每日表情包&#xff1a; 思路&#xff1a; 思路已经很明了了&#xff0c;题目已经给出一般规则和特殊规则&#xff08;而且题目确保给定的是正确的…

利用 ASP.NET Core 开发单机应用

前言 现在是分布式微服务开发的时代&#xff0c;除了小工具和游戏之类刚需本地运行的程序已经很少见到纯单机应用。现在流行的Web应用由于物理隔离天然形成了分布式架构&#xff0c;核心业务由服务器运行&#xff0c;边缘业务由客户端运行。对于消费终端应用&#xff0c;为了应…

Nginx使用详解

简介Nginx的优缺点Nginx的应用场景Nginx支持的模块Nginx模块配置示例1. **HTTP Access Log 模块**2. **HTTP SSL 模块**3. **HTTP Gzip 模块**4. **HTTP Rewrite 模块** Nginx支持的反向代理协议Nginx反向代理配置Nginx反向代理优点Nginx反向代理配置示例Nginx常用配置参数Ngin…

一文搞懂 springboot 如何融合数据源

1、简介 springboot 支持关系型数据库的相关组件进行配置&#xff0c;包括数据源、连接池、事务管理器等的自动配置。降低了数据库使用的难度&#xff0c;除了 mysql 还支持 Derby、H2等嵌入式数据库的自动配置&#xff0c;MongoDB、Redis、elasticsearch等常用的 NoSQL 的数据…

uWSGI、灰度发布、网站数据指标分析、网站限速

1 案例1&#xff1a;部署Python网站项目 1.1 问题 配置Nginx使其可以将动态访问转交给uWSGI&#xff1a; 1.2 方案 安装Python工具及依赖 安装uWSGI并编写配置文件 1.3 步骤 实现此案例需要按照如下步骤进行。 步骤一&#xff1a; 首先$教学资料目录/python拷贝到虚拟…

Python程序设计 函数

简单函数 函数&#xff1a;就是封装了一段可被重复调用执行的代码块。通过此代码块可以实现大量代码的重复使用。 函数的使用包含两个步骤&#xff1a; 定义函数 —— 封装 独立的功能 调用函数 —— 享受 封装 的成果 函数的作用&#xff0c;在开发程序时&#xff0c;使用…

Unity3d Shader篇(一)— 顶点漫反射着色器解析

文章目录 前言一、顶点漫反射着色器是什么&#xff1f;1. 顶点漫反射着色器的工作原理 二、编写顶点漫反射着色器1. 定义属性2. 创建 SubShader3. 编写着色器程序段4. 完成顶点着色器5. 完成片段着色器 三、效果四、总结 前言 在 Unity 中&#xff0c;Shader 可以用来实现各种…