首页

有趣的Canvas,你值得拥有!

seo达人

Canvas 是 HTML5 提供的一个用于展示绘图效果的标签. Canvas 原意为画布, 在 HTML 页面中用于展示绘图效果. 最早 Canvas 是苹果提出的一个方案, 今天已经在大多数浏览器中实现。


canvas 的使用领域


游戏

大数据可视化数据

banner 广告

多媒体

模拟仿真

远程操作

图形编辑

判断浏览器是否支持 canvas 标签


var canvas = document.getElementById('canvas')

if (canvas.getContext) {

console.log('你的浏览器支持Canvas!')

} else {

console.log('你的浏览器不支持Canvas!')

}

canvas 的基本用法

1、使用 canvas 标签, 即可在页面中开辟一格区域,可以设置其宽高,宽高为 300 和 150


<canvas></canvas>

2、获取 dom 元素 canvas


canvas 本身不能绘图. 是使用 JavaScript 来完成绘图. canvas 对象提供了各种绘图用的 api。


var cas = document.querySelector('canvas')

3、通过 cas 获取上下文对象(画布对象!)


var ctx = cas.getContext('2d')

4、通过 ctx 开始画画(设置起点 设置终点 连线-描边 )


ctx.moveTo(10, 10)

ctx.lineTo(100, 100)

ctx.stroke()

绘制线条

设置开始位置: context.moveTo( x, y )

设置终点位置: context.lineTo( x, y )

描边绘制: context.stroke()

填充绘制: context.fill()

闭合路径: context.closePath()

canvas 还可以设置线条的相关属性,如下:


CanvasRenderingContext2D.lineWidth 设置线宽.

CanvasRenderingContext2D.strokeStyle 设置线条颜色.

CanvasRenderingContext2D.lineCap 设置线末端类型,'butt'( 默认 ), 'round', 'square'.

CanvasRenderingContext2D.lineJoin 设置相交线的拐点, 'miter'(默认),'round', 'bevel',

CanvasRenderingContext2D.getLineDash() 获得线段样式数组.

CanvasRenderingContext2D.setLineDash() 设置线段样式.

CanvasRenderingContext2D.lineDashOffset 绘制线段偏移量.

封装一个画矩形的方法


function myRect(ctxTmp, x, y, w, h) {

ctxTmp.moveTo(x, y)

ctxTmp.lineTo(x + w, y)

ctxTmp.lineTo(x + w, y + h)

ctxTmp.lineTo(x, y + h)

ctxTmp.lineTo(x, y)

ctxTmp.stroke()

}


var cas = document.querySelector('canvas')

var ctx = cas.getContext('2d')

myRect(ctx, 50, 50, 200, 200)

绘制矩形

fillRect( x , y , width , height) 填充以(x,y)为起点宽高分别为 width、height 的矩形 默认为黑色

stokeRect( x , y , width , height) 绘制一个空心以(x,y)为起点宽高分别为 width、height 的矩形

clearRect( x, y , width , height ) 清除以(x,y)为起点宽高分别为 width、height 的矩形 为透明

绘制圆弧

绘制圆弧的方法有


CanvasRenderingContext2D.arc()

CanvasRenderingContext2D.arcTo()

6 个参数: x,y(圆心的坐标),半径,起始的弧度(不是角度 deg),结束的弧度,(bool 设置方向 ! )


var cas = document.querySelector('canvas')

var ctx = cas.getContext('2d')


ctx.arc(100, 100, 100, 0, degToArc(360))

ctx.stroke()


// 角度转弧度

function degToArc(num) {

return (Math.PI / 180) * num

}

绘制扇形


var cas = document.querySelector('canvas')

var ctx = cas.getContext('2d')


ctx.arc(300, 300, 200, degToArc(125), degToArc(300))


// 自动连回原点

ctx.closePath()

ctx.stroke()


function degToArc(num) {

return (Math.PI / 180) * num

}

制作画笔

声明一个变量作为标识

鼠标按下的时候,记录起点位置

鼠标移动的时候,开始描绘并连线

鼠标抬起的时候,关闭开关

点击查看效果图


var cas = document.querySelector('canvas')

var ctx = cas.getContext('2d')


var isDraw = false

// 鼠标按下事件

cas.addEventListener('mousedown', function () {

isDraw = true

ctx.beginPath()

})


// 鼠标移动事件

cas.addEventListener('mousemove', function (e) {

if (!isDraw) {

// 没有按下

return

}

// 获取相对于容器内的坐标

var x = e.offsetX

var y = e.offsetY

ctx.lineTo(x, y)

ctx.stroke()

})


cas.addEventListener('mouseup', function () {

// 关闭开关了!

isDraw = false

})

手动涂擦

原理和画布相似,只不过用的是clearRect()方法。


点击查看效果图


var cas = document.querySelector('canvas')

var ctx = cas.getContext('2d')


ctx.fillRect(0, 0, 600, 600)


// 开关

var isClear = false


cas.addEventListener('mousedown', function () {

isClear = true

})


cas.addEventListener('mousemove', function (e) {

if (!isClear) {

return

}

var x = e.offsetX

var y = e.offsetY

var w = 20

var h = 20

ctx.clearRect(x, y, w, h)

})


cas.addEventListener('mouseup', function () {

isClear = false

})

刮刮乐

首先需要设置奖品和画布,将画布置于图片上方盖住,

随机设置生成奖品。

当手触摸移动的时候,可以擦除部分画布,露出奖品区。

点击查看效果图


<div>

<img src="./images/2.jpg" alt="" />

<canvas width="600" height="600"></canvas>

</div>

css


img {

width: 600px;

height: 600px;

position: absolute;

top: 10%;

left: 30%;

}


canvas {

width: 600px;

height: 600px;

position: absolute;

top: 10%;

left: 30%;

border: 1px solid #000;

}

js


var cas = document.querySelector('canvas')

var ctx = cas.getContext('2d')

var img = document.querySelector('img')

// 加一个遮罩层

ctx.fillStyle = '#ccc'

ctx.fillRect(0, 0, cas.width, cas.height)

setImgUrl()

// 开关

var isClear = false

cas.addEventListener('mousedown', function () {

isClear = true

})

cas.addEventListener('mousemove', function (e) {

if (!isClear) {

return

}

var x = e.offsetX

var y = e.offsetY

ctx.clearRect(x, y, 30, 30)

})

cas.addEventListener('mouseup', function () {

isClear = false

})


function setImgUrl() {

var arr = ['./images/1.jpg', './images/2.jpg', './images/3.jpg', './images/4.jpg']

// 0-3

var random = Math.round(Math.random() * 3)

img.src = arr[random]

}

更多demo,请查看 github.com/Michael-lzg…


v-if 和 v-show的区别

前端达人

简单来说,v-if 的初始化较快,但切换代价高;v-show 初始化慢,但切换成本低

1.共同点

都是动态显示DOM元素

2.区别

(1)手段:
v-if是动态的向DOM树内添加或者删除DOM元素;
v-show是通过设置DOM元素的display样式属性控制显隐;
(2)编译过程:
v-if切换有一个局部编译/卸载的过程,切换过程中合适地销毁和重建内部的事件监听和子组件;
v-show只是简单的基于css切换;
(3)编译条件:
v-if是惰性的,如果初始条件为假,则什么也不做;只有在条件第一次变为真时才开始局部编译(编译被缓存?编译被缓存后,然后再切换的时候进行局部卸载);
v-show是在任何条件下(首次条件是否为真)都被编译,然后被缓存,而且DOM元素保留;
(4)性能消耗:
v-if有更高的切换消耗;
v-show有更高的初始渲染消耗;
(5)使用场景:
v-if适合运营条件不大可能改变;
v-show适合频繁切换。



新版vue-router的hooks用法

seo达人

虽然Vue 3还没有正式发布,但是热爱新技术的我早已按捺不住自己的内心,开始尝试在小项目中使用它了。


根据这篇《今日凌晨Vue3 beta版震撼发布,竟然公开支持脚手架项目!》我搭建了一个Vue 3的脚手架项目,用这种方式搭建的脚手架项目不仅仅只有vue是新版的,就连vue-router、vuex都是的。


给大家截一下package.json的图:




可以看到vue-router和vuex都已经开启4.0时代啦!


不过其实我并没有去了解过vue-router 4.0的新用法什么的,因为我觉得它不像vue 3.0都已经进行到beta的版本不会有特别大的变动。


而vue-router 4.0还是alpha的阶段,所以我认为现在去学习它有些为时尚早。但却就是它!差点酿成了一场惨剧。


旧版vue + vue-router的使用方式

假如你在路由里面定义了一个动态参数通常都会这么写:


{

   path: '/:id'

}

然后用编程式导航的时候通常会这样去写:


this.$router.push('/123')

在组件中是这样获取这个参数的:


this.$route.params.id

我以为的新版vue + vue-router的使用方式

由于vue 3.0的Composition API中没有this了,所以我想到了通过获取组件实例的方式来获取$route:


import { defineComponent, getCurrentInstance } from 'vue'


export default defineComponent((props, context) => {

   const { ctx } = getCurrentInstance()

   

   console.log(ctx.$route)

})

没想到打印出来的居然是undefined!

这是咋回事呢?

于是我又打印了一遍ctx(ctx是当前组件上下文):




没有&dollar;的那些字段是我在组件中自己定义的变量,带&dollar;的这些就是vue内置的了,找了半天发现没有&dollar;route了,只剩下了一个&dollar;router,估计vue-router 4.0把当前路由信息都转移到$router里面去了。


带着猜想,我点开了&dollar;router:




currentRoute! 看名字的话感觉应该就是它了!于是乎我:


import { defineComponent, getCurrentInstance } from 'vue'


export default defineComponent((props, context) => {

   const { ctx } = getCurrentInstance()

   

   console.log(ctx.$router.currentRoute.value.params.id)

})

果然获取到了!好开心!


实际的新版vue + vue-router用法

在接下来的过程中我用ctx.&dollar;router代替了原来的this.&dollar;router、用ctx.&dollar;router.currentRoute.value代替了原先的this.&dollar;route。


尽管在接下来的进度中并没有出现任何的bug,程序一直都是按照我所设想的那样去运行的。


但在项目打包后却出现了意想不到的bug:在跳转路由的时候报了一个在undefined上面没有push的错误。


奇了怪了,在开发阶段程序都没有任何的报错怎么一打包就不行了呢?根据我多年的开发经验,我很快就定位到了是vue-router的错误。


难道这样写是错的吗?可是我打印了ctx,它里面明明有一个&dollar;router、&dollar;router里面明明就有currentRoute、currentRoute里面明明就有一个value、value里面明明就有params、params里面我一点开明明就看到了传过来的参数啊:




估计可能是vue-router的bug,果然alpha阶段的产物不靠谱,我开始后悔使用新版的vue脚手架项目了。


vue-router里的hooks

不过这时我突然灵光一现,vue 3不是受到了react hooks的启发才产生了Composition API的吗?


那么估计vue-router肯定也会受到react-router的启发了!


还好我学过react,果然技多不压身啊!估计里面肯定是有一个useXxx,就像这样:


import { useXxx } from 'vue-router'

那么应该是use什么呢?按理来说应该会尽量的和以前的API保持一定的联系,我猜应该是useRoute和useRouter吧!


为了验证我的想法,我打开了node_modules找到了vue-router的源码:




果不其然,在第2454和第2455行我发现它导出了useRoute和useRouter,那么就是它了:


import { defineComponent } from 'vue'

import { useRoute, useRouter } from 'vue-router'


export default defineComponent(_ => {

   const route = useRoute()

   const router = useRouter()


   console.log(route.params.id)

   router.push('/xxx/xxx')

})

使用这种方式不但可以成功跳转路由,也同样可以获取到路由传过来的参数,这次再打包试了一下,果然就没有之前的那个报错了。


结语

估计以后的vue全家桶要开启全民hooks的时代了,在翻看源码的同时我发现他们把一些示例都写在了vue-router/playground文件夹下了,在里面我发现了一些有趣的用法。


如果有时间的话我会仔细研究一下然后出一篇更加深入的文章给大家,当然如果已经有小伙伴等不及我出新文章的话可以直接进入vue-router-next的github地址:


https://github.com/vuejs/vue-router-next

它的示例都放在了playground这个文件夹下,期待你们研究明白后出一篇更加深入的文章!

B 端设计师如何做竞品分析?

雪涛

将要分析的竞品排了个期,从最难最不熟悉的开始。为什么从最难的开始,可能是个人习惯吧,吃掉最难的那个,后面就会更上手。突然想起之前读的一本书「吃掉那只青蛙」,很不错的一本书,有时间去温习下。

一个产品,其实会有很多功能点,有核心的主要功能,也有一些辅助功能,也会有一些让你忽略,但关键时刻很需要的应急功能,而这些点都需要去整理出来。

分析前-熟悉产品

这一点很重要,要先熟悉产品。如果对产品都不熟悉,那还是先不要做竞品分析。因为很难判断竞品的功能和风格是否也适合当前产品,因为对产品的不熟悉,会产生误判。

当然,产品的目标人群,产品定位,适用范围等等,都会影响产品分析。

所以,花时间熟悉自己负责的产品,是不能跳过的。

开始前的准备

1. 制定时间规划

最好事先做好时间规划,可以有一整块的时间,这样分析产品时,思绪也会比较完整和连续,可以更专注。计算大概分析一个产品需要花费的时间,最好不要用零碎时间来做,这样只会增加时间上的代价,也会增加挫折感;

2. 确定分析的目的

在「竞品分析」中,想要得到的结论和重点是什么。比如重点可能是产品的报表功能、产品的代码审核功能等等,目的的确定能让分析更有针对性,减少干扰。无目的随意分析,得到的结果也会是零乱不堪,最后只是在浪费时间。

3. 寻找帮助者

每个产品,都有其不一样的特性和产品逻辑,你不一定能够完全 cover 到,甚至有些点就是比较难理解的,特别是偏技术性的名词,这时若有技术同学的帮助,就会如虎添翼。所以最好可以事先找一位产品相关的技术同学,询问这段时间是否有空,帮助你解答一些问题。

个人建议:能够在网上查到的资料,就不要先问人,除非时间成本特别高。一方面也是提升自己解决问题的能力,另一方面,也是节省彼此的时间。对方愿意帮你解决问题,不代表你要把所有问题一股脑倒给他,自己了解后再问,也是对对方的尊重,大家的时间都同样宝贵。

4. 其他tips

如果是内部公司产品,提前确认是否需要权限,提前申请好,减少正式开始后,还要等待审批时间。外部产品可以提前找好网站,可以咨询的客服入口,如果是付费竞品,咨询是否可以向财务申请报销等等。

好,现在正式开始吧!

1. 像个用户一样去使用产品

很多时候,设计师的职业病,会让我们过多注重视觉享受,而忽略作为用户,想要的有时候只是功能可用。今天不管你把「扫一扫」功能做得多美,美得像个艺术品一样,可是当扫码付款的时候,怎么也扫不出来,那种站在店家前面忐忑不安,怎么也无法完成付款,后面一堆人等你,你仿佛听见后面其他顾客窃窃私语地讨论着发生什么事情。那种场景我相信你不想经历,同样我们也不应该让用户来经历。

我的项目主管,一直都有提醒我,要像个小白来使用和设计我们的产品。这句建议,也一直在提醒着我。如果站在高姿态来俯视用户,我们就很难真正的「懂」用户,进而很难设计出真正满足用户需要的产品。

这是竞品分析,但是我们也需要转换自己的角色,变成用户。这样能更明白究竟竞品带给用户是便利,还是麻烦。有时适时抽离「设计师」的角色,会让你更能去体会用户的感受。

所以,先去用这个产品吧,然后才会有然后。

2. 如何去使用竞品

一个产品的使用,总是有它的使用场景,手机端的就更多样了,简直无所不在。B 端产品可能会相对少,一般是在办公场景或是特定场景。

可以像个编剧一样,给自己写点剧本,加点情节,塑造一个角色,假设竞品是电商方向,你可以想像,自己是一个刚毕业的社会新人,你可能没多少钱,你可能刚拿到你人生第一桶金,你想买件衣服犒劳自己,或许你会是数码控,你关注已久的佳能单反在双 11 中有优惠等等,然后再去预想接下去的情节,在购物方面会考虑的问题,或许是好用,或许是有趣等等。

也可以做任务式去使用产品,比如以电商为例,任务可以是买件喜欢的衣服,从搜索产品,到找到喜欢的衣服,添加购物车,提交订单,等待发货,收货,确认收货。这一个完整的流程走下来,就会体验产品功能是否好用,搜索结果是否符合预期等等。

3. 记录

使用产品的过程中,会遇到很多情况,有些是可预期的,有些是不可预期的。有些让人觉得很好用,有些却会让人受挫。将这些情况都记录下来,有助于分析产品的可用性程度和满意度。

  • 愉快的:可能是一个友好的提示,减轻你的认知负担,也可以是一个贴心小 loading 动画等等
  • 受挫的:点击没有反馈,提交后没反馈,不知道执行成功与否等等
  • 难以理解:产品中专业名词太多,没有附带解释和帮助文档,完全不知其所以然
  • 产生误解:以为是 A,结果是 B
  • 一脸懵:页面太乱,不知从哪里下手

上面这些只是举例说明,在竞品当中可能遇到的一些问题,也可以去反思自己的产品是否也会这样让用户感到困惑。有时候,太熟悉自己的产品,会自认为产品很完美,会理所当然认为「大家都这么认为」……

记录问题、原因,感受并截图为证(有必要可录屏),后期可追溯。写得越详细越好,后面整理的时候会更清晰。

4. 各个击破-功能了解

在熟悉整个产品后,就需要对产品的各个功能进行分析了解、梳理。了解竞品的核心功能是什么,核心功能在解决用户什么问题,是否真的解决了用户的痛点,其他功能又在整个产品当中充当什么样的角色。

将竞品的功能与本产品功能对比,不只是对比有无,更进一步地去想,为什么有这个功能,为什么没有这个功能,有或没有是否会提高用户的使用效率,用户的留存,用户的体验等等。

功能多不代表好,如果功能不能给用户带来益处,其实它的存在只是增加开发成本而已。

整体总结

其实竞品分析中,最难的是总结归纳。做了一堆的分析后,结论是什么呢,这个结论如何写呢?

可以先从设立分析目的开始,找到中心轴线,然后再慢慢延展开来。在要做总结报告时,你会欣喜地发现最初设立目标是多么的重要。

文章来源:优设    作者:箴盐设计

这10个设计原则,是确保金融类产品体验优秀的核心要义

雪涛

1、

如何让邮件体验设计更加吸引人?

雪涛

互联网时代的人们早就受够了信息爆炸,我们每天都会经系统推送、应用通知、微信、电话、短信等各类渠道收到大量消息。有多久你没有查收自己的邮箱?就算打开邮件,又有多少推荐内容让你有兴趣进一步了解?是 EDM 老了没用了?真正的原因,可能是我们一开始就错误地忽视了 EDM 设计。

对于 95 后以及更年轻的群体来说,EDM 确实是个上了年纪的概念。EDM(Email Direct Marketing)也叫 Email 营销、电子邮件营销。企业向目标客户发送 EDM 邮件,建立同目标客户的沟通渠道,向其直接传达相关信息,用来促进销售转化。

这个起源于上世纪 80 年代中期,正式诞生于 90 年代的早期互联网产物现在已经三十多岁了。时至今日,EDM 早已成为了全球公认的网络营销重要方法之一,其卓越效果为互联网人数十年的实践所证实。但 EDM 在我国的应用还处于非常低级的水平,不仅没有系统的理论,在实践中也存在许多误区。

在这样一个重视审美与强调更新及时的时代,EDM 邮件朴实无华的外表与「一旦发出就固定呈现」的内容特质显得有些格格不入。作为用户体验设计师,我们可以做什么让 EDM 不落伍呢?

避免成为垃圾邮件

首先,我们可以在设计层面上避免 EDM 邮件被邮箱软件识别为垃圾邮件,不带敏感词语或内容、淡化商业广告色彩、减少数字与附件使用都有助于降低被邮箱系统屏蔽的风险。我们更可以在全量发送前,对指定邮箱进行小范围测试以确保邮件发送成功率。

其次,从其历史来源来看,早期的 EDM 来源于垃圾邮件,这使人们对其本能地缺乏好感,存在排斥心理。因此 EDM 的节奏和时机必须做好控制,对邮件发送的各类数据做好统计,掌握用户的阅读习惯,能更好地提升邮件的打开率。

保持最佳邮件格式

邮件内容需要设计为一定的格式来发送,常用的邮件格式包括纯文本格式、HTML 格式和 Rich Media 格式,或者是这些格式的组合。一般来说,HTML 格式和 Rich Media 格式的电子邮件比纯文本格式具有更好的体验效果。但 Rich Media 格式的电子邮件易造成邮件过大,并且无法确保用户在客户端均能够正常显示,所以在设计时我们优先选择 HTML 格式邮件。

确保跨端体验

与网页不同,我们无法针对不同设备做邮件内容相应的适配设计,兼顾设备特性的通用模版也就成为了设计时的必要关注点。对用户来说,一封邮件阅读体验很差,那么无论邮件的内容多么精彩、多么吸引人,最终的结果也可能只会被丢弃在一边。因此,我们通常会按照移动端尺寸对邮件界面进行设计,注意字体大小、最佳尺寸以及链接按钮的大小等。

除此以外,邮件中链接的定义也应得到我们充分的重视。由于邮件中的链接我们同样无法预先针对不同打开设备进行单独编辑,在有条件的情况下我们可以对链接所跳转的页面进行响应式设计以确保高质量的跨端浏览体验,或者我们也可以采用默认跳转路径而后重定向的传统方式。

与「我」紧密相关

EDM 营销与一般的营销方式最大的区别是:EDM 是一对一的沟通,让用户感觉到尊重,让他感觉到这是为他所建立并且是他所独享的沟通方式。在标题、正文的文案上强调「我」,在内容上也应如此。用户在意什么,我们就发送什么。把握住用户关注的信息,帮助用户收集支持 TA 做决策所需的信息。当我们发送邮件给用户,给予其操作行为的反馈或提醒时,不要浪费这最好的营销机会。优先提供给用户与之行为或特征相关的服务与帮助,其次通过个性化服务或产品推荐促进购买或注册转化,有助于我们将营销机会转化为实际销售成果。

兼顾质量和效率

做好个性化对 EDM 内容模型要求颇高,但从设计角度讲,我们完全可以以原子设计思维实现邮件内容模块的低成本创建与复用。以通用设计模块为「壳」,内容与组合规则为「核」,快速响应 EDM 的运营需求。

以上 5 点就是我结合近期项目经验所得。EDM 虽老,但设计可以让 EDM 老而弥新。祝经你精心设计的 EDM 邮件,一经发出,封封有回应

文章来源:优设    作者:鱼子酱聊设计

用4个经典的重量级产品案例,告诉你什么是标杆式体验设计

雪涛

今天和大家聊一个很多朋友常年卡在 P5/P6 需要关心的命题——如何从业务出发打造具有商业价值还能兼顾用户体验的设计,此篇不谈理论,就通过 4 个经典的重量级产品案例就给大家安排明白啥是「一拳超人」式体验设计——就一个字「强」。

滴滴出行-xpanel

滴滴出行应该属于大家的高频使用 app,但是使用的功能一般还是集中在叫车流程,所以大家可能不太会关注到 CDX 设计团队一个非常核心的设计成果——xpanel。

简单来说 xpanel 就是一个附着于第一信息架构层级上,垂直 Y 轴且支持 X 轴拓展滑动的 Feed 卡片位。内容上分为「消息卡片」「主体卡片」「拓展卡片」三个维度,首屏保障除了「消息」与「主体」外三分之一「拓展卡片1」的露出。

但在简单的交互背后蕴藏的是基于业务的 UGD(用户增长设计)设计思考,这里引用 2018IXDC 会上滴滴主讲人的原话来说就是:

对特定场景垂直领域的深耕和挖掘,寻找「接触点」,帮助获取更多的功能、内容、服务、特性、品牌、运营甚至是喜好……进而实现业务的「有效增长」(转化、变现、留存)。

通俗一点解释就是 xpanel 利用主卡与拓展卡之间的信息架构关系,把拓展卡平衡的分为几类,比如「与产品功能相关的卡片」「与运营相关的卡片」等。

把本来被 LBS 地图一屏内抢占的空间通过简易的交互模式补偿回来了,这样既不打破用户的核心体验 focus 在地图与主卡上,同时又增强了运营、功能的玩法与拓展,可谓双赢。

根据这几年滴滴 xpanel 的线上应用,拓展卡片基本挖掘涵盖了以下场景的露出:优惠福利、出现卡券、会员体系、安全相关、出行提醒、拉新导流、运营活动等,未来可拓展的价值内容会更多。看着各路出行类 app 又纷纷长期沿用 xpanel 的设计,想必线上的数据反馈应该也是很正向的。

抖音-TopView

在上篇文章《多维度解析 | 抖音vs快手的产品设计策略差异》中的商业化模块里简要提及过抖音的 Topiew 超级广告位,这里单独拿出来和大家解析一下它究竟有多6。

从功能角度看,它是一个从开屏延续到端内视频信息流的广告位,占据了用户从进入抖音的第一视觉。

从交互角度看,topview 主要展现以开屏沉浸式视频 3s 播放→淡出互动转化组件 3s(完美融入原生视频信息流),剩余操作手势与功能等同原生视频信息流。

在这样一个有着 1 亿+第一曝光的产品位置,单纯只做常规静态开屏稳当入账不香吗?事实是抖音确实让它不香了,没有创新就没有新的收获。基于业务和当前产品形态下的交互模式使抖音有一个天时地利的优势——沉浸式体验,在这样的交互模式下给视频化的开屏提供了很好的承接入口。从开屏开启到融入信息流,在交互形态的切换中又为广告内容的播放时长赢得了更多时间。

更可怕的一点是 3s 播放后融入原生视频信息流中的 TopView 除了正常收割广告转化带来的单量,还可以通过右侧的主页链接轻松引流进行粉丝沉淀(今天就算你不买,先关注我,成为我的潜在用户,来日我再推一个新商品视频,你可以第一时间看见也许感兴趣就买单了)。

说完这些大家仔细回忆一下平常我们接触的有视频广告的视频平台,别说 60s、30s,15s 我们都嫌长,但为啥 TopView 显得相对没那么惹人烦呢(上次留的思考题)?个人认为除了抖音在选择合作品牌时会倾向符合平台气质的品牌合作(细数它合作过的品牌:Mac、宝马、林肯、vivo 等)保障广告质量和提供「跳过」外,直接融入信息淡出的互动组件会不仅会给用户新奇感,还会激发用户的互动欲望。

最后看一组数据(与宝马合作数据),曝光数:1.1 亿+;有效播放率:53.82%;点击率:13.26%。所以你猜一个最长可以展示 60s 的品牌视频内容、同时进行品牌粉丝沉淀、良好体验带来更高有效播放的亿级曝光广告位能值多少钱?

淘宝-二楼

2016 年淘宝启动了一个项目要做一款内容化栏目——以视频为主,每晚更新一期,类比「一千零一夜」的故事。

那么在满满当当的淘宝运营区里该选择哪一个来试玩这个有趣的「新栏目」呢?是在头部的 10 宫格里再挤进去一个图标呢?还是在热门推荐里挤出一个 tab 呢?还是做一个悬浮的右下角的运营位?显然都不太合适。

根据这款产品每晚 6 点钟才可以使用,早上 7 点就会消失的游戏规则,最适配它的入口是一个不占界面原生空间,同时又有一定仪式感的位置。于是下拉 loading 的大空区成为了设计师们考虑的阵地。

△ 不知道这个banner为什么要排挤我

但地方选好了,又有了新顾虑。因为 iOS 的用户基本被系统洗脑了下拉手势,对于他们来说下拉=刷新,贸然在下拉刷新的手势基础上再叠加一个无关联的结果显然是有风险的。因此从交互上需要界定 2 个维度的指标来保障新栏目的体验。

  • 下拉速度(速度临界值:速度多快?→刷新,多慢?→新栏目)——以速度为优先衡量指标(只要速度快,拉的距离再大也是→刷新)
  • 下拉距离(距离临界值:拉到多少距离进入新栏目?)——兼顾单手用户操作难度

反复试错 2 个指标数据的实际体验之后,新栏目有了安身之所,赐名「二楼」。进入「二楼」的整体交互和现在的短视频产品玩法基本雷同,全屏竖滑切换,小图标带货。下拉加载位的开发,从普通 loading 动效到运营位的植入基本被各类电商平台轻松复刻了,因此这一切看上去更没什么了得,但对于原创来说那毕竟是 4 年前。

豆瓣-叠加上滑板

谈到豆瓣我算是半个老用户了,豆瓣自身是个比较复杂的集合多条业务线分支(「小组」「同城」「阅读」「音影」……)的多生态产品,这里我们主要拿它 18 年 6.0 大改版中影音模块的详情页大改造来说事儿。

△ 可能有很多人已经忘记6.0前的豆瓣电影详情页长啥样了,带你回顾一下。

看完对比图,视力正常的朋友乍一看都能看出 6.0 版详情页整容得有多成功。但具体成功在哪里,可能不仅仅是好看这么简单。

大背景从海报上智能取色虽然不算是什么稀奇的做法,但是加了适度的渐变应用在这里也可以说是非常的恰到好处了。另外深底色和视觉比重加大的外链区都突显了「第三方播放」与「购票选座」的视觉感知。让用户沉浸在电影详情中并引导他们走向「豆瓣的主要收入来源之一——电影票分销与第三方视频播放产品引流」正好是 6.0 豆瓣改版一个「小小的目标」——更务实(商业化)。

从交互层面看,且不说评论头部吸底这个事情是不是也是因为 6.0 商业化的影响(评论区增加「话题」进行重点运营),这个交互本身我觉得还是很强大的。强大的体现在于良好的空间收纳能力与信息拓展能力。我给它起了个好听的名字叫-叠加上滑板(不好听也认了吧,毕竟也没有内部人员告诉我他们是不是起名字了)

这里可能又会有很多人质疑它与用户已洗脑的上滑手势之间的冲突,这点解释起来和上文淘宝「二楼」有些类似,区别是豆瓣并没有做上滑速度 or 距离的临界值,只是把滑动区域做了隔离。而对比它的效仿者 boss 直聘,人家倒是在交互上做了进一步优化,适配自己的产品情况做了上滑叠层卡隐藏和上滑距离临界值。

这个故事告诉我们,要抄也要抄得比人家的交互更优秀才不丢人昂。

文章来源:优设    作者:Nana的设计锦囊

前端实现生成带有样式的excel表格 Node和浏览器读写Excel文件探究实践

seo达人

最近碰到个需要自动生成表格的任务,作为前端的我,就想在 node 和浏览器中生成强大的表格,所以特此研究了很多关于表格的 npm 库

支持读写 Excel 的 node.js 模块

node-xlsx: 基于 Node.js 解析 excel 文件数据及生成 excel 文件,仅支持 xlsx 格式文件

js-xlsx: 目前 Github 上 star 数量最多的处理 Excel 的库,支持解析多种格式表格 XLSX / XLSM / XLSB / XLS / CSV,解析采用纯 js 实现,写入需要依赖 nodejs 或者 FileSaver.js 实现生成写入 Excel,可以生成子表 Excel,功能强大,但上手难度稍大。不提供基础设置 Excel 表格 api 例单元格宽度,文档有些乱,不适合快速上手;普通版本不支持定义字体、颜色、背景色等,有这个功能需要的可以使用 pro 版,是要联系客服收费的,害我照着 API 设置调试了好多次都失败。好在样式设置问题有一些教程,通过研究本人已解决,可设置宽度颜色等等,见根目录本人修改的 xlsx.js

xlsx-style 基于 xlsx 封装的样式库,可以在 xlsx 的基础上设置样式。样式不全,宽度都设置不了,好多年前作者就不维护了.宽度设置问题本人已解决了,见修改的 xlsx-style.js 文件

exceljs 在使用此库之前,本人已花费了很大的精力,用以上库做好了表格,但是发现不能设置页眉页脚,添加图片,打印选项设置等等,直到发现了这个库,文档齐全,功能强大,并且还免费.但是star较少,差一点就错过了。本教程主要针对这个库

代码库地址

https://github.com/lingxiaoyi/excel

安装

npm install


npm install -g nodemon


调试使用,替代 node 命令,实现保存文件,node 自动重新启动执行,必须全局安装才能运行


使用

nodemon app.js


js-xlsx 具体 api 使用方法请参考 main.js demo 使用,app.js 中修改为 require('./src/main.js');

exceljs 具体 api 使用方法请参考 main-exceljs.js demo 使用,app.js 中修改为 require('./src/main-exceljs.js');

因为每次生成完表格,每次都需要打开表格查看样式,在 windows 电脑中,打开表格之后就锁定不能生成新文件了,本来想着能导出一个 html 文件对应表格的样式


node 调试

vscode 中打开调试右侧设置编辑,将下方代码复制进去,点 nodemon 启动就可以进行 debug 调试了


{

     "type": "node",

     "request": "launch",

     "name": "nodemon",

     "runtimeExecutable": "nodemon",

     "program": "${workspaceFolder}/app.js",

     "restart": true,

     "console": "integratedTerminal",

     "internalConsoleOptions": "neverOpen",

     "skipFiles": ["<node_internals>/**"]

   },

webpack 目录的作用

每次生成完新表格,都需要重新打开表格查看样式,在 windows 电脑中,打开表格之后就锁定了,再次生成新表格就会报错,文件已锁定,不能写入,对于想偷懒的我,能不能实现像 webpack 热更新功能那种,修改样式 js 页面自动更新呢?


wps 自带另存 html 文件功能,但是没有提供生成的 api ,网上也搜索不到对应的转换功能,

本来以为自己要实现一套表格转 html 的功能。通过不断尝试,偶然间发现手机浏览器可以直接打开预览 xlsx 文件,内心狂喜啊


使用方法

进入 webpack 目录安装依赖包,安装好之后执行


npm run dev


启动成功之后,会自动打开带有 ip 地址的预览地址,此时在电脑浏览器会自动下载 xlsx 文件,忽略不管,用手机直接打开此地址,就能看到 xlsx 表格的内容了,并且每次新修改内容和样式,都会自动刷新页面显示新表格.


小技巧

谷歌浏览器插件:


生成二维码的插件生成二维码方便手机扫描

划词翻译 用来翻译一些看不懂的英文文档

browser 目录

浏览器中实现生成 xlsx 表格方法


进入 browser 目录安装依赖包,安装好之后执行


npm run dev


启动成功之后,拖动根目录 src 下的李四表格到页面上的输入框里,成功生成表格之后会生成一个下载链接地址,右键在新标签页打开链接,即会生成一个新的表格文件出来,完整 api 使用和 demo 文件请参考 index.js


vue 和 react 用法可以参考此例子,如果有必要也可以此版本库的例子


一些概念

在使用这个库之前,先介绍库中的一些概念。


workbook 对象,指的是整份 Excel 文档。我们在使用 js-xlsx 读取 Excel 文档之后就会获得 workbook 对象。

worksheet 对象,指的是 Excel 文档中的表。我们知道一份 Excel 文档中可以包含很多张表,而每张表对应的就是 worksheet 对象。

cell 对象,指的就是 worksheet 中的单元格,一个单元格就是一个 cell 对象。

xlsx 使用注意事项

constXLSX = require('xlsx');

let html = XLSX.utils.sheet_to_html(workbook.Sheets.Sheet1)

生成 html 的用法,并且不会有任何样式


exceljs 使用注意

读取文件问题

因为 exceljs 读取文件不支持 sync 同步读取,给的实例也是 await 例子.导致我读取完遇到一个问题,就是老是生成不成功,最后发现必须要把所有逻辑全部放入函数中,像下方这样


(async function (params) {

 let res = await workbook.xlsx.readFile(`${__dirname}/赵六.xlsx`);

 //执行所有数据处理逻辑

 //执行写的逻辑

 workbook.xlsx.writeFile(path.resolve(__dirname, '../webpack/test222.xlsx'));

});

所有逻辑全部要写入这个函数中,这样本来是可以的,但是出错调试几率较大,并且读取到的数据庞大还需要额外处理,所以我读取数据逻辑就用的 node-xlsx,十分简单方便,如果你用的 exceljs 读取文件数据出现问题,大概率是异步同步逻辑搞错了,多加注意即可

宽度设置

列宽不知道是以什么为单位,反正不是像素(已测量),例子中是以厘米为单位再乘以 4.7 的结果设置的,4.7 是不断测试的结果.

快捷查看列宽的方法,打开 wps 表格,长按列与列字母间的竖线,就能看到列宽,取厘米的单位即可.见下图




前景色

前景色设置必须右键单元格选择设置单元格格式,然后选择图案样式选择颜色,就可以前景色填充

worksheet.getCell('A2').fill = { type: 'pattern', pattern:'darkTrellis', fgColor:{argb:'FFFFFF00'}, bgColor:{argb:'FF0000FF'} };


背景色

worksheet.getCell('A2').fill = { type: "pattern", pattern: "solid", fgColor: { argb: next.bgColor }, }


排版不一致的问题

解决 Mac 下编辑 Microsoft Office Word 文档与 Windows 排版不一致的问题,,不同的系统用 wps 打开相同的表格,打印预览的时候,表格宽度显示不一样

问题详细说明地址


我的解决办法就是 mac 下显示正常,按 mac 下的宽度来设置就可以了


参考资料

exceljs

node-xlsx

js-xlsx

日历

链接

blogger

蓝蓝 http://www.lanlanwork.com

存档