tech share
  • tech-share
  • Engineering
    • 登录鉴权
    • SSR 页面路由
    • npm 版本号
    • 缓存
    • 数据库容灾
    • 动态效果导出 gif
    • Chrome-devtools
    • C 端 H5 性能优化
    • Docker
    • Monorepo 最佳实践
    • 技术架构演化
    • 项目规范最佳实践
    • snowpack
    • 静态资源重试
    • 前端页面渲染分析
    • Git
    • 前端重构
    • 微前端
    • 项目依赖分析
    • 前端监控原理
    • webpack
    • BS 架构与 CS 架构
    • HTTPS
    • package-lock.json 生成逻辑
    • SVN(Subversion)
    • 数据库分类
    • gulp
    • 前端架构
    • Bundle & Bundless
    • 控制反转 IoC
  • JavaScript
    • Javascript 性能
    • JavaScript 原型(2) - 原型与原型链
    • JavaScript 原型(1) - 构造函数
    • JavaScript - Promise
    • ES6 解构赋值
    • 前端离线化
    • Proxy
    • Object.defineProperty()简介
    • TypeScript
  • MachineLearning
    • GAN生成对抗网络
    • 虚拟对抗训练
    • 深度度量学习
    • 原型网络
    • PyTorch优化器
    • 隐马尔可夫模型2
    • Shapley Value 算法
    • Embarassingly Autoencoder算法
    • AutoRec算法及其后续发展
    • 深度学习常用激活函数
    • 序列预测ConvTran算法
    • 联邦学习
    • 深度学习推荐系统算法整理
    • 隐马尔可夫模型
    • 黎曼优化方法
    • FM算法
    • 机器学习常见评价指标
    • VAE算法
    • Adam优化器详解
    • Transformer算法
    • Self-attention 推荐算法
    • CNN 卷积神经网络
    • 图嵌入
    • 集成学习算法
    • RecBole开源框架
    • NCE-PLRec
    • 深度学习初始化方法
    • RNN循环神经网络
    • PyTorch数据处理
    • PyTorch安装和基本操作
    • XGBoost算法
    • NCF算法与简单MF的对比
    • 计算最佳传输
  • CSS
    • 什么是BFC
    • 纯CSS实现可拖动布局
    • 滚动穿透解决方案
  • React
    • React 生命周期
    • React Ref
    • React Hooks
    • SWR
    • React 数据流
    • React 函数式组件和类组件的区别
  • 可视化
    • OffscreenCanvas
    • Echarts 平滑曲线端点为什么不平滑
    • 颜色空间
    • 词云布局解析
    • 3D 数学基础
    • Canvas 图片处理
    • GLGL ES
    • WebGL 中绘制直线
    • Graphics API
    • 现代计算机图形学基础
    • Canvas 灰度
  • Vue
    • Vue2.x全局挂载整理
    • Vue2.6.x源码阅读
      • Vue2.6.x源码阅读 - 2.目录结构分析
      • Vue2.6.x源码阅读 - 4.源码阅读-platform
      • Vue2.6.x源码阅读 - 1.准备工作
      • Vue2.6.x源码阅读 - 5.源码阅读-core-Vue构造函数
      • Vue2.6.x源码阅读 - 7.源码阅读-core-响应式原理
      • Vue2.6.x源码阅读 - 3.源码阅读-shared
      • Vue2.6.x源码阅读 - 6.源码阅读-core-组件挂载
    • Vue + TypeScript Web应用实践
    • Vue2.x指令
    • nextTick()的使用
    • vue-cli2.x 的使用与项目结构分析
    • Vue响应式原理及总结
    • VueX的使用
    • Electron-Vue + Python 桌面应用实践
    • Vite
    • Vue组件通信整理
    • 记录一个问题的探索过程
  • Linux
    • memcg
  • GameDev
    • 游戏中的几种投影视图
    • 从零开始写软渲染器06
    • 从零开始写软渲染器05
    • 从零开始写软渲染器04
    • 从零开始写软渲染器03
    • 从零开始写软渲染器02
    • 从零开始写软渲染器01
    • 从零开始写软渲染器00
    • 现代游戏常用的几种寻路方案(一)
  • Node
    • NPM Dependency
    • Node 优势
    • Node Stream
    • Node 模块系统
  • HTML
    • html5语义与结构元素
  • 跨端
    • Flutter 介绍
  • Golang
    • Golang 基础
  • AR
    • SceneKit
由 GitBook 提供支持
在本页
  • 背景
  • 分析
  • SVG 导出图片
  • SVG 导出 Gif
  • 添加文字

这有帮助吗?

  1. Engineering

动态效果导出 gif

上一页数据库容灾下一页Chrome-devtools

最后更新于4年前

这有帮助吗?

背景

开发需求中有动态地图的形式来展现。需要将地图导出成图片,而且需要支持 gif 图,从而可以更好地展现,最终效果类似如下效果:

分析

地图一般存在两种展现模式,一种为静态查看,需要下载成图片以便查看;另一种为动态查看,用户可以调整播放速度与以及手动暂停/播放,需要下载成 gif 来查看。

SVG 导出图片

SVG 导出图片主要可以分为四步:

  1. 获取想要下载的 svg 元素

  2. 将 svg 转为 data64

  3. 使用 canvas drawImage 方法,加载 data64 画在 canvas 上

  4. 将 canvas 转为 data64,作为 a 标签 href,模拟 a 标签点击,进行下载

const img = new Image();
img.onload = drawImage;
img.onerror = errorSolve;
const b64Start = "data:image/svg+xml;base64,";

const svgStr = btoa(
  unescape(encodeURIComponent(new XMLSerializer().serializeToString(svg)))
);

img.src = `${b64Start}${svgStr}`;

SVG 导出 Gif

将动态效果导出为 gif 主要的思路是获取 svg 在不同时刻每一帧的图像,再根据这些图像以不同的间隔来保存为 gif。

但是动态地图的下载要求下载所有的阶段,用户的播放和暂停不应该影响下载的 gif 的结果。也就是实际下载的 gif 与实际展现的 svg 并不同步,无法简单通过获取网页的 svg 来进行转化。因此,需要一个额外的容器来承载渲染的结果,当用户进行下载时,将每一阶段渲染到一个 canvas 容器中。然后使用 gif.js 库,将每一帧加入 gif 中,当全部帧都加入后,对 gif 进行下载。

比较麻烦的一点是 gif.js 必须指定一个 worker,worker 可以放到项目的静态文件中,也可以放在静态资源托管服务上,先获取自己 worker 的源码,再创建一个 base64 地址填写进去。

const gif = new Gif({
  quality: 10,
  debug: true,
  workers: 2,
  workerScript: URL.createObjectURL(workerBlob),
});
gif.addFrame(canvas, { delay: timeInterval });
gif.on("finished", function (blob) {
  const url = URL.createObjectURL(blob);
  const link = document.createElement("a");
  link.setAttribute("download", "demo.gif");
  link.href = url;
  link.click();
});

gif.render();

添加文字

添加文字 可以使用 canvas context 的 fillText 方法,使用 measureText 方法可以计算出文字的宽度,然后根据宽度定位到右上角。

const ctx = canvas.getContext("2d");
const margin = 20;
ctx.fillStyle = "#bbb";
ctx.font = `bold ${fontSize}px Calibri`;
ctx.textBaseline = "top";
const { width } = ctx.measureText(text);
const x = canvas.width - (width + margin);
ctx.fillText(text, x, margin);