推广 热搜: csgo  vue  angelababy  2023  gps  新车  htc  落地  app  p2p 

【第1961期】如何用Canvas拍出JDer's工作照

   2023-08-25 网络整理佚名1490
核心提示:99%的模块是由图片构成,因此预加载图片这一功能必不可少。这样当我们想移动这个角色时,通过移动容器,来保证整体性。图片可以加载了,可是当我想做拖拽等操作时,又又又报错了。对长按保存图片支持不太充分,因此临时决定在其中屏蔽此功能,这里尝试了三种方法:在解决了上面一系列的问题之后,要回到最初的分析:不管项目用了何种技术,最终呈现的本质都是图片。

前言

给老板、超级老板拍照的文化还是很有趣的。 今天的晨读文章由京东用户体验设计部@杜守钟贡献分享。

@京东用户体验设计部-前端开发部目前拥有前端开发人员约50人,主要为京东零售提供WEB前端开发、APP RN开发、小程序开发、小游戏开发、H5开发等能力支持集团与京东健康

正文从这里开始~~

背景

在京东,工作五年的老员工被称为“大老板”,工作十年的则被称为“超级老板”。

自2016年5月19日起,每年的这一天被定为京东集团“519退伍军人节”。 俗话说:五年磨银,十年锻金! 在京东成长10年的员工,在行业任何一家公司都可以像金子一样闪闪发光!

在这5年、10年无数个日日夜夜的奋斗中,你是以什么样的姿态工作的? 让我来揭秘这些姿势是如何修炼的吧~

怎么玩

首先我们用gif来回顾一下效果

游戏的基本步骤如下

好了,拍完照片就可以分享到朋友圈了。

技术选型

可以看到这里使用了大量的图片。 通过图片的拖拽、缩放等操作,放置角色和饰品,最终合成对应的图片。 那么这个过程是如何实现的呢?

首先,我们使用NUTUI来构建整个项目,它的脚手架可以很好地处理图像优化和打包。 底部操作菜单模块使用NUTUI中的Tab组件,提高了开发效率。 在主界面部分,该库基于一个轻量级的触摸屏设备手势库。 使用js进行开发。

努图伊

NUTUI是一个京东风格的移动组件库,为移动Web界面开发并服务企业级前、中、后端产品。 50+优质组件,40+京东移动项目正在使用,支持按需加载,支持服务端渲染(Vue SSR)...

扫描二维码即可体验

.js

是一个开源代码库,可识别触摸、鼠标和 . 它没有任何依赖,而且非常小,压缩后只有 7.34 kB。它支持常见的单点和多点触摸手势,并且可以添加自定义手势

.js

它是一套基于HTML5开发的模块化库和工具。 基于这些库,可以非常快速地开发基于HTML5的游戏、动画和交互式应用程序。

包含以下部分

本项目中主要使用它,并结合Tween.js制作一些小动画。

了解完所使用的技术之后,我们来看一下具体的实现过程:

实施计划

该项目主要包括三个核心:加载图片、绘制姿势、手势操作。 下面我们分别讨论一下。

加载图像

由于本项目中99%的模块都是由图片组成的,所以预加载图片的功能是必不可少的。 图片这么多,需要手动一一列出来加载吗? 当然不是! 现在是机械化时代,能交给工具的人就不去做。

依靠文件的读写来完成图片列表文件的自动生成,加载时只需按顺序加载此列表下的图片即可。

画姿势

承接‘画图’的能力,这里使用的是画图和画文字的API。一般的画图步骤是:创建舞台->创建对象->设置对象属性->添加对象到舞台->更新舞台以呈现下一帧

提供了两种渲染模式,一种是使用,一种是使用e,默认是,帧数为20,这里我们选择e模式,因为需要对页面元素进行很多操作,选择这种方式会更顺利。

其他基本设置

该事件默认不支持触摸设备,需要手动启用

portant;border-width: 1px !important;border-style: solid !important;border-color: rgb(226, 226, 226) !important;">
  1. createjs.Touch.enable(this.stage);

实时刷新舞台

portant;border-width: 1px !important;border-style: solid !important;border-color: rgb(226, 226, 226) !important;">
  1. createjs.Ticker.addEventListener("tick", this.stage.update(event));

.js 配置

由于.js默认不启用事件,因此需要在选项中使用它来设置识别器

准备工作完成,下面正式开始

绘制场景

为维护文明形象,不支持站在办公桌上工作。 因此,将场景分为背景和桌子两部分,通过设置人物上层桌子的层级来设置约束。

首先画出背景

注意,如果不是第一张图,需要清除之前的内容

portant;border-width: 1px !important;border-style: solid !important;border-color: rgb(226, 226, 226) !important;">
  1. this.stage.removeAllChildren();

用同样的方法画出表格。 需要注意的是,表格绘制完成后,需要设置其level

portant;border-width: 1px !important;border-style: solid !important;border-color: rgb(226, 226, 226) !important;">
  1. ...

  2. this.stage.addChild(deskImg);

  3. this.stage.setChildIndex(deskImg, 1);

画人物

绘制人物和场景不同,所以这里需要用到它。 是一个容器,可以包含 Text、Shape 和其他元素。 例如,您可以将手臂、腿、躯干和头部放在一起,将它们转换为一个组,同时仍然相对于彼此移动各部分。 这里我们把人物和表情合二为一,方便统一管理,统一移动、缩放、旋转等。

在绘制角色之前,我们首先确定绘制位置:默认位置在画布的中间

portant;border-width: 1px !important;border-style: solid !important;border-color: rgb(226, 226, 226) !important;">
  1. let pos = {

  2. x: this.canvasW / 2,

  3. y: this.canvasH / 2,

  4. };

如果您已经选择了角色,需要更改时需要保留之前角色的位置

portant;border-width: 1px !important;border-style: solid !important;border-color: rgb(226, 226, 226) !important;">
  1. pos = {

  2. x: joy.x,

  3. y: joy.y,

  4. };

下面是具体的绘制步骤:

画表情符号

绘制上面的角色时,会创建一个带有名称的容器,我们也将表达式绘制到其中

portant;border-width: 1px !important;border-style: solid !important;border-color: rgb(226, 226, 226) !important;">
  1. var face = new createjs.Bitmap(imgBg);

  2. ...

  3. joyContainer.addChild(face);

这样当我们想要移动这个角色的时候,就可以通过移动容器来保证完整性。 否则,头部将无法跟上身体的运动。 。 。

删除元素

添加角色后,会记录当前操作对象。 当删除按钮被触发时,只需找到它并删除其相关内容即可。

手势操作

.js 是一个用于检测触摸手势的库,支持最常见的单点和多点触摸手势,并且可以完全扩展以添加自定义手势。 该功能将集成在NUTUI中,并在下一个版本中正式发布。

通过监听该事件,我们可以获得当前操作的缩放和旋转数据,然后与之前的状态结合起来,就可以实现各种手势操作的效果。

好了,一切准备就绪,开始你的表演吧~

首先选择一个办公场景,然后进行角色扮演,站着累了吗? 没关系,换个姿势坐下就可以了,当然,如果你想站在凳子上也没关系。 。 表情是不是有点老套了? 然后伸出你的舌头。 在电脑水杯的排列上,最后有一句标语“京东增肥20斤”。 。

你玩得开心吗? 好吧,别着急,我们继续讨论如何实现。

生成图像

当您点击“完成”后,我们将进入分享页面。 分享页面底图随机选择三种颜色。 这里我们需要创建一个临时的来绘制共享图片,将共享背景、自定义的姿势场景图(通过.方法转成图片)、二维码、昵称依次绘制到这个临时的图片中,然后最后图片导出后,赋值给共享图片的url。

由于共享图片和共享页面的显示元素并不完全相同,所以向用户展示的是共享页面,并且共享图片的透明度设置为0,只能保存,看不到。

然而事情并没有那么简单,一大波bug正在马不停蹄地涌来。 。

路由底部导航移除遇到的问题

前面说过,这个项目由加载页面和主界面两个页面组成,中间有路由跳转(模式)。 但在某些手机中,通过路由跳转到另一个页面时,导航模块会自动出现在底部。 这是我们不愿意看到的。 在已经拉伸的空间里,不可能有这么大的一块。 宽容是存在的。

因此,称重后选择了模式,但进入主界面后,用户无法返回加载页面,不可能两者兼得。

ios中输入框不自动缩回,有白块

加载完成后,有一个昵称的输入框。 ios下输入完成后,关闭键盘后页面底部会出现一大片空白,卡住。

但是当我们在页面上随机滑动时,这个白色块就会消失。 这是因为ios键盘弹出后,整个页面会被推上来,所以我们需要使用一个函数,在模糊键盘落下时滚动页面,让页面回到原来的位置。

portant;border-width: 1px !important;border-style: solid !important;border-color: rgb(226, 226, 226) !important;">
  1. blur() {

  2. window.scrollTo(0, 0);

  3. }

自从系统更新后,白色方块变得透明,让人更加难以看清。 显然他们什么也看不到,但输入框就是无法选择。 别以为脱了马甲就不认识你了,上面的解决办法还是有效的。

图片跨域

本地开发完成,代码上传到服务器后,原本宁静的世界消失了,取而代之的是耀眼的红色:

经过一番查找,我发现了以下一段话:

虽然可以在画布中使用未经 CORS 批准的图像,但这样做会污染画布。 一旦画布被污染,就无法再从画布中提取数据。 例如,()、()或()方法不能再使用; 这样做会引发安全错误。 这样可以防止用户未经许可使用图像从远程网站获取信息而暴露私人数据。

这就解释了上述错误的由来,那么如何解决呢?

portant;border-width: 1px !important;border-style: solid !important;border-color: rgb(226, 226, 226) !important;">
  1. var bg = newImage();

  2. bg.crossOrigin = "Anonymous";

这样在镜像加载过程中就会开启CORS功能,从而绕过错误报告。

点击报告错误

图片可以加载,但是当我想做拖拽之类的操作时,又报错。 。 。

提供点击区域。 您可以设置另一个对象objB作为显示对象objA,点击objB相当于点击objA。 这个objB不需要添加到显示对象列表中,也不需要可见,但它将在交互事件的触发中替换objA。

给对象绑定一个点击区域,这样拖动就是操作这个区域而不是原图,这样就不会报错

等级制度

在本项目的设置中,角色位于所有其他元素的底部,并且当元素切换和选择时,还需要将当前选择的元素放在顶部。 这里使用的方法

方法允许您在显示列表中向上或向下移动显示对象。 显示列表可以看成一个数组,索引位置从0开始。如果创建3个元素,那么它们的位置就是第0层、第1层、第2层。 第 2 层上的对象位于外部,第 0 层上的对象位于内部。

如果想要将某个元素移动到所有元素的顶部,此时就需要使用该属性,它的含义是容器中显示的对象数量。 最外层深度为-1层。 对于原始等级高于顶级元素的其他元素,相应的等级将降低一级。

portant;border-width: 1px !important;border-style: solid !important;border-color: rgb(226, 226, 226) !important;">
  1. if (ele.name === "joy") {

  2. this.stage.setChildIndex(ele, 1);

  3. } else {

  4. this.stage.setChildIndex(ele, this.stage.getNumChildren() - 2);

  5. }

当我们选择或者添加一个元素时,就会触发级别设置,因为需要保证当前操作的元素级别在最上面。 因为有顶层元素,所以在设置层级时,如果是人物元素,那么就设置在第二层,仅比场景的背景层高; 如果是其他元素,则设置为第二顶层。

低版本ios有问题

测试阶段发现ios10以下的手机无法拖动,这真是晴天霹雳!

在调查的过程中,我发现了一些奇怪的事情。 我无法拖放,因为复选框上的删除按钮未加载。 这个按钮有什么特别之处? 哦,原来是配置中的url——自动将小图片转换成格式,按照这个思路,去掉这个功能后,问题就解决了,但是没有细讲。

接下来的结果更糟糕,共享图片消失了,只剩下一个背景框!

正如上面“生成图像”部分提到的,图像将被导出,导出格式正是上述有问题的格式。

我们发现在ios10以下的版本中,该事件无法触发,而是消失了。 那么图片可以转换成什么格式呢? 答案在这里:

我们首先将图像转换为blob格式

然后通过URL生成。 方法

portant;border-width: 1px !important;border-style: solid !important;border-color: rgb(226, 226, 226) !important;">
  1. window.URL.revokeObjectURL(sharePhoto);

由于返回的 url 会存储在内存中,直到触发事件(例如:关闭)。 所以我们要养成一个好习惯,用完记得放掉哦~

那么什么是神圣的呢? 我们一起来学习一下:

定义: URL.() 方法将根据传入的参数创建一个指向参数对象的 URL。该 URL 只存在于创建它的文档中。 新对象 URL 指向执行的 File 对象或 Blob 对象。

返回一个带有哈希值的url,它会被存储在内存中,直到触发事件(例如:关闭)或执行释放。

浏览器支持如下,移动端基本可以放心使用~

防止长按事件

即将上线时,由于内部App对于长按保存图片的支持不够,所以暂时决定在里面屏蔽这个功能。 以下是尝试过的三种方法:

portant;border-width: 1px !important;border-style: solid !important;border-color: rgb(226, 226, 226) !important;">
  1. document.oncontextmenu = (e) => {

  2. e.preventDefault();

  3. };

适用于网络浏览器,但不适用于移动设备

实践证明这种方法并不可行,我们依次分析一下:

user-控制用户是否可以选择文本,而我们这里需要的是控制图片。

--touch-:触摸并按住触摸目标时禁用或显示系统默认菜单。适用于:新窗口打开等链接元素、保存图像等img元素等。

乍一看,这不正是我们所需要的吗?

但是,--touch- 是一个非规范属性 ( ),未出现在 CSS 规范草案中。

看一下支持情况就明白了:

最终我选择了第一种方法,简单直接,不用考虑兼容性。

图像优化

解决了以上一系列问题之后,就要回到最初的分析:无论项目中使用了什么技术,最终呈现的本质还是图片。 因此,图像的大小不仅影响加载速度,还影响渲染速度。 为了提供更好的用户体验,选择使用NUTUI中的图像压缩功能,它可以提供高压缩比的图像优化,并且可以自动转换为webp格式。 大家都知道webp格式的图片比普通的压缩图片要小很多。 有如此强大的后盾,想要不出众都难!

总结

无论你是老板、超级老板,还是刚刚加入京东的新鲜血液,519老员工节都是属于每一个京东人的节日!

在做项目的过程中,我从头开始学习,在项目中间不断尝试、犯错误,不断解决问题,学习新知识,收获很多。 在今后的工作中,还要注重基础知识的广度,不断积累。 也许学习的时候应用场景不太清楚,但是有一天你会发现每一种知识都有它存在的理由。

推荐给你

 
反对 0举报 0 收藏 0 打赏 0评论 0
 
更多>同类资讯
推荐图文
推荐资讯
点击排行
网站首页  |  关于我们  |  联系方式  |  使用协议  |  版权隐私  |  网站地图  |  排名推广  |  广告服务  |  积分换礼  |  网站留言  |  RSS订阅  |  违规举报
Powered By DESTOON