惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

L
LangChain Blog
The GitHub Blog
The GitHub Blog
Recent Announcements
Recent Announcements
MyScale Blog
MyScale Blog
P
Proofpoint News Feed
S
Security @ Cisco Blogs
N
News and Events Feed by Topic
H
Hacker News: Front Page
Attack and Defense Labs
Attack and Defense Labs
S
Secure Thoughts
Microsoft Security Blog
Microsoft Security Blog
N
Netflix TechBlog - Medium
U
Unit 42
Stack Overflow Blog
Stack Overflow Blog
T
Threat Research - Cisco Blogs
Google Online Security Blog
Google Online Security Blog
Spread Privacy
Spread Privacy
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
Exploit-DB.com RSS Feed
Exploit-DB.com RSS Feed
L
LINUX DO - 热门话题
T
Tenable Blog
博客园 - 叶小钗
D
DataBreaches.Net
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
博客园_首页
人人都是产品经理
人人都是产品经理
aimingoo的专栏
aimingoo的专栏
C
Check Point Blog
博客园 - 三生石上(FineUI控件)
量子位
P
Proofpoint News Feed
H
Help Net Security
Blog — PlanetScale
Blog — PlanetScale
宝玉的分享
宝玉的分享
Recorded Future
Recorded Future
The Register - Security
The Register - Security
F
Fortinet All Blogs
Engineering at Meta
Engineering at Meta
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
Last Week in AI
Last Week in AI
S
Schneier on Security
V
Vulnerabilities – Threatpost
雷峰网
雷峰网
Microsoft Azure Blog
Microsoft Azure Blog
G
GRAHAM CLULEY
G
Google Developers Blog
月光博客
月光博客
V
V2EX
T
Troy Hunt's Blog
A
Arctic Wolf

蚊子的前端博客

微说 | 有的人觉得只要不考XX,一定能考好!-蚊子的前端博客 微说 | 春天来了-蚊子的前端博客 明天和意外不知道哪个先来-蚊子的前端博客 微说 | 既然错过了路口,就及时止损,重新规划路线上路-蚊子的前端博客 微说 | 2025年的出生人口数是792万-蚊子的前端博客 微说 | 工作年终总结,让写对接的接口的数量?-蚊子的前端博客 微说 | 温家宝:没有政治体制改革的成功 经济体制改革不可能进行到底-蚊子的前端博客 微说 | 违法犯罪了该不该被禁言?-蚊子的前端博客 微说 | 不明白为什么在推荐去俄罗斯旅游-蚊子的前端博客 微说 | 易中天论骗子-蚊子的前端博客 微说 | 一场大风吹散了秋天-蚊子的前端博客 微说 | 既要、又要、还要、更要!-蚊子的前端博客 又是一年的国庆雨季-蚊子的前端博客 微说 | 预估下2025年的出生人口数据-蚊子的前端博客 微说 | 可惜了我那些小时候的书本-蚊子的前端博客 微说 | 牛马有的是,驴不够了!-蚊子的前端博客 微说 | 能否有一条非户口也能高考的路-蚊子的前端博客 微说 | 小聊中医-蚊子的前端博客 微说 | 将要制作的一款新产品-蚊子的前端博客 微说 | 做人要有信-蚊子的前端博客 微说 | 好一个正义联盟-蚊子的前端博客 微说 | 都是见过吃过的主儿-蚊子的前端博客 微说 | 追求8小时工作制有错吗?-蚊子的前端博客 微说 | 如果尖锐的批评完全消失-蚊子的前端博客 微说 | 很好!-蚊子的前端博客 微说 | 封禁用户可以,但要告知具体原因-蚊子的前端博客 微说 | 面朝大海,春暖花开-蚊子的前端博客 微说 | 程序员的悲哀是什么?-蚊子的前端博客 微说 | 2025年出生人口的预测-蚊子的前端博客 前端在 LiveKit 中如何获取所有的参与者-蚊子的前端博客 在 Electron 中使用 LiveKit 实现屏幕共享-蚊子的前端博客 在 Electron 应用中,如何获取 Mac 的摄像头和麦克风权限-蚊子的前端博客 微说 | 不要总让台湾艺人表态-蚊子的前端博客 微说 | 他们总说要更开放-蚊子的前端博客 自己封装 uuid 踩的一个小坑-蚊子的前端博客 CKEditor5 光标在粘贴的图片下一行-蚊子的前端博客 软考出成绩了,没过-蚊子的前端博客 微说 | 也许有一天我们这个社会会进步-蚊子的前端博客 好久没有更新文章了-蚊子的前端博客 微说 | 中共中央关于进一步全面深化改革,推进中国式现代化的决定-蚊子的前端博客 微说 | 关于惩罚性赔偿-蚊子的前端博客 微说 | 生孩子是谁的事儿!-蚊子的前端博客 微说 | 给群众一些选择电视剧的空间-蚊子的前端博客 微说 | 活着-蚊子的前端博客 微说 | 借用睡前消息里面的一段话-蚊子的前端博客 微说 | 曾经梦寐以求的,现在却没了追求-蚊子的前端博客 微说 | 我小时候的那些书本-蚊子的前端博客 给博客添加了一个微说的功能-蚊子的前端博客 今年的双 11 很平静-蚊子的前端博客 微说 | 贪就是贪-蚊子的前端博客 微说 | 总有人说穿越小说为什么不发展工业和科技!-蚊子的前端博客 微说 | 花了很多时间和精力,-蚊子的前端博客 2025 年终于是增加了 2 天的假期-蚊子的前端博客 微说 | 这是我的第一条微说,-蚊子的前端博客 如何手写 Array 的 forEach 方法-蚊子的前端博客 看到聚美优品的没落,真是唏嘘-蚊子的前端博客 如何手写 Array 的 map 方法-蚊子的前端博客 秋意正浓时-蚊子的前端博客 收藏几个常用但访问比较慢的组件的官网网址-蚊子的前端博客 从 Axios 源码分析如何支持 fetch 方法的-蚊子的前端博客 1024,给博客评论区所有的头像戴上一顶可爱的帽子-蚊子的前端博客 使用 GitHub 搭建了一个静态博客-蚊子的前端博客 在 JavaScript 如何判断变量是否为空-蚊子的前端博客 我为什么还在坚持我的个人博客-蚊子的前端博客 不要再手动拼接 URL 参数,请使用 URLSearchParams-蚊子的前端博客 使用 NodeJs 向百度资源推送链接-蚊子的前端博客 工具链极度内卷,留给开发者的性能优化手段已经不多了-蚊子的前端博客 在 React 中显示多个标签,超出省略并可以 hover 显示更多-蚊子的前端博客 蚊子的前端博客:探索与分享前端技术的奇妙之旅-蚊子的前端博客 互联网程序员前景真的一片黑暗吗?-蚊子的前端博客 Deno2.0 正式发布,向 Node.js 兼容-蚊子的前端博客 React 请求数据别再使用 useEffect 和 useState,试试 SWR 吧!-蚊子的前端博客 React 的 useEffect 的一些使用场景和技巧-蚊子的前端博客 前端趣闻之 JavaScript 语言的诞生和发展-蚊子的前端博客 如何合并同一接口的相同参数的请求-蚊子的前端博客 百度搜索中关于蚊子前端博客的奇怪的检索数据-蚊子的前端博客 使用 React 实现 todo list 的 curd 操作-蚊子的前端博客 React 模板中为什么可以用逻辑与运算符-蚊子的前端博客 评论回复在一张表里的评论系统如何进行分页-蚊子的前端博客 React 组件多次调用时如何区分不同的 div 容器-蚊子的前端博客 安全赋值运算符,再也不用写 try-catch 了-蚊子的前端博客 使用 nextjs 重构我的个人博客-蚊子的前端博客 基于 gitlab 的 webhook 向飞书发通知-蚊子的前端博客 数字转换为更高量级单位的工具方法-蚊子的前端博客 如何避免旧请求的数据覆盖掉最新请求-蚊子的前端博客 JavaScript 中 toString 的冷知识-蚊子的前端博客 React 中 useState 和 useRef 与全局变量的区别-蚊子的前端博客 从面试官的角度分析下简历中存在的问题-蚊子的前端博客 使用 React 实现 6 个输入框的短信验证码功能-蚊子的前端博客 反驳那些要实时刷新页面的前端部署方案-蚊子的前端博客 JSON stringify 的一些不常见使用-蚊子的前端博客 JavaScript 中数组 Array 的常见操作-蚊子的前端博客 前端如何提升用户的交互体验-蚊子的前端博客 没有日的日期在iOS中报 Invalid Date 的探究-蚊子的前端博客 飞书很好,但还是没抗住-蚊子的前端博客 给 Antd 的 DatePicker 组件实现带有至今的功能-蚊子的前端博客 antd 的 InputNumber 输入框添加左右的加减按钮-蚊子的前端博客 给想进国央企的同学介绍一款招聘软件「国聘」-蚊子的前端博客 基于 React 和 antd 实现的图片裁剪压缩功能-蚊子的前端博客 uniapp 中 checkbox 中的 checked 不生效的方案-蚊子的前端博客
记一次项目中分享图片的过程-蚊子的前端博客
author · 2019-05-20 · via 蚊子的前端博客

我们的项目主要是在新闻客户端中进行使用,会经常调用客户端提供的jsapi完成各种操作。前些天我们上线了一个非常重要的功能:分享图片

通常分享图片到微信的步骤比较繁琐:先长按保存图片,然后打开微信,点击朋友圈分享,然后在相册里选择刚才保存的图片,最终分享成功。而现在新上线的分享图片功能,能够直接从新闻客户端分享到微信朋友圈,省略了中间保存图片和选择图片的过程。

生成分享图片-蚊子的博客

在新闻客户端里的页面分享到微信朋友圈,从前端角度可以拆分为以下几个步骤:

  1. 跨域的图片地址转为base64;
  2. 图片对应的链接转为二维码的base64地址;
  3. 使用html2canvas将DOM结构转为base64图片地址;
  4. 使用websocket将base64地址传到后台,然后返回CDN地址;
  5. 调用客户端提供分享图片的jsapi,将4中的CDN地址传进去,呼起分享;

将跨域的图片转为base64是因为在canvas通过toDataURL()方法转换时,跨域的图片是无法转换的,必须将其转为本地图片才可以。对于第4步中将base64转为线上的CDN地址,则是因为客户端jsapi的要求,无法直接传递base64地址。

在使用html2canvas把DOM结构转为图片的过程中,踩过2个坑:1. 无法使用圆角,无法使用透明的背景,最开始打算分享的图片是圆角的,结果生成的图片是圆角的,同时背景里某个地方是镂空的,结果生成的图片里,变成了白的背景色;2. 边框border无法使用虚线,我在某个地方使用border: 2px dashed #888,结果生成的图片里变成了实线。

我们上面分享图片是一个依赖过程,当前步骤须依赖上一个步骤的完成情况,必须上一个步骤成功了,才能执行当前步骤。中间哪个步骤出错了,都无法成功分享图片,那么这里做的兜底操作是分享文章型链接。

this.$emit('upProgress', 0); // 进度为0,表示分享图片的流程开始
try {
    let p1 = img2base64byApi();
    let p2 = img2base64byApi();
    let p3 = qrcode();

    await Promise.all([p1, p2, p3]);
    this.$emit('upProgress', 1); // 进度为1,将跨域的图片全部转为base64

    let base64Url = await convertHtml2Canvas('.shareimg');
    this.$emit('upProgress', 2); // 进度2,将dom结构转为base64图片

    let imgurl = await upImgByWs(base64Url);
    this.$emit('upProgress', 3); // 进度3,base64图片地址转为CDN地址成功

    await showBigImageShareMenu(imgurl);
    this.$emit('upProgress', 4, imgurl); // 进度4,呼起图片的分享成功
} catch(e) {
    // 上面任意一个步骤出错,均认为分享图片失败
    // 转为分享文章
    console.error(e);
    this.$emit('upProgress', -1); // 进度-1,分享图片失败
}

在实现img2base64byApi, qrcode, convertHtml2Canvas这些方法的过程中,注意对缓存的使用,因为相同的图片地址最终转换后的base64图片也是一样的。因此,将某个图片转换base64成功后,将原图片和转换后的base64图片地址缓存起来,待下次再次调用时,直接返回即可,避免了不必要地重新转换。

const qrcode = (str: string): Promise<string> => {
    if (cache.getCache(str)) {
        return Promise.resolve(cache.getCache(str));
    }
    return new Promise((resolve, reject) => {
        QRCode.toDataURL(str)
            .then((code: string) => {
                cache.setCache(str, code);
                resolve(code);
            })
            .catch(reject);
    });
};

总结一下,在实现这个功能中,也是耗费了不少地时间,遇到了一些问题。主要是Promise的合理使用,同时注重缓存的重要性。