xiangpaopao / blog Goto Github PK
View Code? Open in Web Editor NEW插屁屁提高班
插屁屁提高班
test blog
作为iOS中的新技术,Storyboard(以下简称SB)的优点和缺点同样明显,关于纯手写和StoryBoard间的博弈,推荐阅读一下 http://onevcat.com/2013/12/code-vs-xib-vs-storyboard/ 。而本文主要是要讲如何在SB中控制更多的样式。
作为一个web前端开发者,习惯了使用灵活的HTML+CSS控制页面,做iOS开发时,要创建大量类、写大量代码来构建视图,有时还不得不和业务代码混在一起,这一点很不爽。之前在SB上做过一些尝试,但是SB上提供的选项实在不够用,比如想给一个按钮设置圆角都没有选项,以往我都是创建一个新类,写了一行代码:btn.layer.cornerRadius = 10;
直到我在objc.io上看到了这篇文章 Behaviors in iOS Apps,才明白原来可以通过User Defined Runtime Attributrs 来给视图修改样式。例如我需要给button设置圆角,那就添加一条layer.cornerRadius
并填上合适的值。
自定义属性支持以下数据类型
OK,我们来尝试给按钮添加边框
问题来了,按钮怎么也不显示边框。原因是layer的borderColor
属性类型是CGColor而不是Color,所以我们得转换,可以通过创建一个Category来让把Color转换为CGColor
CALayer+XibConfiguration.h
#import <QuartzCore/QuartzCore.h>
#import <UIKit/UIKit.h>
@interface CALayer(XibConfiguration)
@property(nonatomic, assign) UIColor* borderColorFromUIColor;
@property(nonatomic, assign) UIColor* shadowColorFromUIColor;
@end
CALayer+XibConfiguration.m
#import "CALayer+XibConfiguration.h"
@implementation CALayer(XibConfiguration)
-(UIColor*)borderColorFromUIColor
{
return [UIColor colorWithCGColor:self.borderColor];
}
-(UIColor*)shadowColorFromUIColor
{
return [UIColor colorWithCGColor:self.shadowColor];
}
-(void)setBorderColorFromUIColor:(UIColor*)color
{
self.borderColor = color.CGColor;
}
-(void)setShadowColorFromUIColor:(UIColor*)color
{
self.shadowColor = color.CGColor;
}
@end
然后把刚才的borderColor
替换为borderColorFromUIColor
重新运行代码,发现边框正常显示了。
那如果只需要四边边框宽度不一样怎么办呢?CSS中我们是这样写的border-width: 5px 10px 2px 10px;
,为了iOS中也能这样写,我写了一个方法来实现。
-(NSString*)borderWidths
{
return self.borderWidths;
}
-(void)setBorderWidths:(NSString*)borderWidths
{
NSString *widthsStr = [[borderWidths stringByReplacingOccurrencesOfString:@"{" withString:@""]
stringByReplacingOccurrencesOfString:@"}" withString:@""];
//widths:[top,right,bottom,left]
NSArray *widths = [widthsStr componentsSeparatedByString:@","];
float topWidth = [[widths objectAtIndex:0] floatValue];
float rightWidth = [[widths objectAtIndex:1] floatValue];
float bottomWidth = [[widths objectAtIndex:2] floatValue];
float leftWidth = [[widths objectAtIndex:3] floatValue];
if (topWidth!=0) {
CALayer *border = [CALayer layer];
border.backgroundColor = self.borderColor;
border.frame = CGRectMake(0, 0, self.frame.size.width, topWidth);
[self addSublayer:border];
}
if (rightWidth!=0) {
CALayer *border = [CALayer layer];
border.backgroundColor = self.borderColor;
border.frame = CGRectMake(self.frame.size.width - rightWidth, 0, rightWidth, self.frame.size.height);
[self addSublayer:border];
}
if (bottomWidth!=0) {
CALayer *border = [CALayer layer];
border.backgroundColor = self.borderColor;
border.frame = CGRectMake(0, self.frame.size.height - bottomWidth, self.frame.size.width, bottomWidth);
[self addSublayer:border];
}
if (leftWidth!=0) {
CALayer *border = [CALayer layer];
border.backgroundColor = self.borderColor;
border.frame = CGRectMake(0, 0, leftWidth, self.frame.size.height);
[self addSublayer:border];
}
}
把他添加到CALayer+XibConfiguration.m就可以在SB中通过layer.borderWidths
来设置不同宽度的边框了。
这里我只是提供了这个思路,类似的还有很多样式都可以通过这种方式控制,减少代码量。刚才的demo可以从 https://github.com/xiangpaopao/ChangeStyleInSB 获取。
移动端经常会有这样的展示页面,页面中有一些独立的元素,并且伴随各种交互效果、转场动画。
针对页面布局的话,大概有这样的需求:
这种形式页面在正常的文档流中布局显然不太合适,比较传统的做法是这样:
比如这样一个640*960的设计稿,分背景和按钮两层,要求背景全屏(可以不显示全)按钮定位在精确的位置上。
一般会把背景图用background-size:cover显示在一个宽高撑满屏幕的容器上以适应各种屏幕, 这个按钮怎么定位呢,一般是会这样做吧position:absolute;bottom:18%;
可以看到iPhone4中按钮错位,原因是当屏幕尺寸改变时,背景图与按钮的尺寸与位置会遵循不同的规则变化。这个例子中的效果貌似还能接受,可有些场景对定位的要求可能比这更高。
解决这个问题一般会用媒体查询针对屏幕做适配吧
@media screen and (max-height: 500px) {
.btn{
bottom:12%;
}
}
Demo 在这里 http://jsbin.com/zugape/edit
如果页面中元素多、定位要求精确,这种做法是比较暴力的。下面是支付宝十年晒单页面中针对不同尺寸屏幕做的适配
/* <=640 */
@media screen and (min-width: 319px) and (max-width: 321px) {
...
}
/* 720 */
@media screen and (min-width: 359px) and (max-width: 361px) {
...
}
/* 1024*768, 2056*1536 */
@media screen and (min-width: 383px) and (max-width: 385px) {
...
}
/* 800 */
@media screen and (min-width: 399px) and (max-width: 401px) {
...
}
/* 1080 */
@media screen and (min-width: 539px) and (max-width: 541px) {
...
}
/* 1200 */
@media screen and (min-width: 599px) and (max-width: 601px) {
...
}
/* 1440 */
@media screen and (min-width: 719px) and (max-width: 721px) {
...
}
/* 1600 */
@media screen and (min-width: 799px) and (max-width: 801px) {
...
}
/* for small screen */
@media screen and (max-height: 500px) {
...
}
/* for iPhone6 Plus */
@media only screen and (device-width: 414px) and (-webkit-min-device-pixel-ratio: 3),
only screen and (device-width: 375px) and (-webkit-min-device-pixel-ratio: 3){
...
}
总结下这种方式的缺点:
一张图片如果给他设置<img src="pic" style="max-width: 100%;max-height:100%” />
的话,无论视口大小,他都会显示在视口里面,如图
类似的还有图片作为背景显示时的background-size属性。
background-size: contain:缩放图像的最大值,其宽度和高度都能放入内容区域
background-size: cover:缩放图像的最小值,其宽度和高度都能放入内容区域
当你改变图片的宽度或高度时,图片的另一边会自动按比例缩放,如果div有这种能力多好啊!
vw、vh是CSS3中出现的新的长度单位,vw 相对于视窗的宽度,视窗宽度是100vw;vh 相对于视窗的高度,视窗高度是100vh。
其中视窗指的是浏览器实际显示区域,即window.innerWidth/window.innerHeight的大小。
如 想一个元素的宽是视口宽的一半,只需设置width:50vw
乍一看好像和百分比类似,并没发现多少优势,在进行一些尝试之后我总结了一下他们的异同
百分比 | vw、vh |
---|---|
相对父级元素 | 相对视口 |
横向/纵向相对父级横向/纵向 | vw/vh相对视口宽/高(即写vh就是相对视口高、写vw就是相对视口宽,不必横向与横向纵向与纵向对应) |
对字体大小无效 | 对字体大小有效 |
Tips:其实margin-top/bottom以百分比为单位时他的参照对象并不是父元素的height而是width。详情阅读 doyoe margin系列文章
这些特性可以实现一些之前用CSS难以解决的问题。
由于百分比高度是相对父级的,之前如果想让文档流中的某容器相对视口高度100%,只用css的话得从html>body>element一层层下来都设置height:100%
,而现在只需给元素设置height:100vh
。
height 100% Demo http://jsbin.com/risegu/edit
先上Demo
百分比方式 http://jsbin.com/vemudo/edit
vw方式 http://jsbin.com/huwido/edit
这两个Demo建议把浏览器模拟成移动设备的样子,拉伸浏览器窗口宽度观察效果。
可以看到当浏览器宽度改变时,百分比Demo中只有元素的宽度发生了变化,而统一使用vw做单位的Demo中文字的大小、元素的高度与宽度均按缩放比例发生了变化,这正是我们想要的“像图片一样布局”的效果。
还有vw、vh延生出来的单位vmin、vmax。
vmin:关于视口高度和宽度两者的最小值
vmax:关于视口高度和宽度两者的最大值
我觉得这是一组神奇的单位,但是暂时对这个使用场景的理解有限,能想到的场景是一个正方形,height:100vmin;width:100vmin
时,得到的是这个视口中能显示满的一个最大正方形(http://jsbin.com/baheko);height:100vmax;width:100vmax
时得到的是能把这个视口全部显示满的最小正方形(http://jsbin.com/tamasi)。
然后在codepen上找到这样一个demo,他让16:9的视频窗口在视口中自适应 http://codepen.io/CreativeJuiz/pen/KzkgL
其他适用场景有待发掘
vw vh 在桌面端浏览器兼容性已经是比较好了,不过在移动端貌似还需要等待,国产安卓浏览器的话UC到现在都不支持真不让人省心。
另外对张鑫旭的视区相关单位vw, vh..简介以及可实际应用场景 中他的结论:vw vh只适用于非定位元素;vh高度值的内部元素不支持百分比%高度 有些疑问,我在测试中并未发现这样的情况,这篇文章写于2012年,不知道是不是这样的情况只发生在当时的浏览器。Demo http://jsbin.com/dineka/edit
回头来看我们的布局,vw vh的方案因为兼容性问题只能放弃,重新分析下我们的需求:
其实我们需要那么一个全局的相对单位...
我们可以通过修改这个单位来控制整个布局...
我们可以通过判断屏幕的比例来缩放整个页面以展示全部内容...
然后想到类似手机淘宝的动态rem的方案:所有布局元素单位使用rem,由于rem是相对于根元素的,所以不同屏幕按一定规则控制的font-size 即可兼容各屏幕。
var root = document.getElementsByTagName('html')[0],
NATIVE_W = 640;
function updateSize() {
var w = window.innerWidth;
var cw = w / (NATIVE_W / 100);
root.style.fontSize = cw + 'px';
}
window.onload = updateSize;
window.onresize = updateSize;
这样的页面一般会有设计稿的,我们需要设置rem和设计稿中px的转换比例。
1rem = 1px 这样固然方便,但是有些浏览器有最小字体的限制。
所以我想让 1rem = 100px。
我们的设计稿一般为640 x 960,这个尺寸是针对iPhone4,设备像素比是2,那么我们实际需要html的font-size为50px。
这样的话假如设计稿中一个元素尺寸为240px x 200px,
那么这个元素需要在屏宽为320px的设备上显示的尺寸是120px x 100px,而我们需要他在布局时设置的宽高是2.4rem x 2rem。
设计稿宽640px 也就是整个页面应为 6.4rem。 所以对应不同宽度的设备时用window.innerWidth(视口宽) / 6.4 得到的就是对应此设备的font-size。
布局时,把设计稿上的尺寸除以100然后把px单位换成rem。
怎么解决比较短的屏幕显示不完全的情况呢,可以先判断一下视口的比例,当视口高宽比低于设计稿宽高比时,缩小html节点的font-size这样就能把内容显示全了
var root = document.getElementsByTagName('html')[0],
NATIVE_W = 640,
NATIVE_H = 960;
function htmlSize() {
var cw = 50,
w = window.innerWidth,
h = window.innerHeight;
if ((w / h) > (NATIVE_W / NATIVE_H)) {
cw = h / (NATIVE_H / 100);
} else {
cw = w / (NATIVE_W / 100);
}
root.style.fontSize = cw + 'px';
}
window.onload = htmlSize;
window.onresize = htmlSize;
Demo http://jsbin.com/rupole/edit
可以试下缩小、拉大窗口布局也会像图片那样相应变化,并且无论窗口是什么样的尺寸,元素都会出现在比较合理的位置,当然,一些特殊情况仍然要用媒体查询来适配。
然后在布局时,其实只要把这段js放在页面中,就rem当成px来用就行了,不需要多考虑啥,这个方案的优点就是构建页面成本低,跨屏显示效果也能满意。
另外有些元素我们想他垂直方向上针对中心定位而不是顶部或底部,比如想让一个小球居中之前大概是这样:
#cir{
width:200px;
height:200px;
border-radius:100px;
background:#888;
position:absolute;
left:50%;
top:50%;
margin-left:-100px;
margin-top:-100px;
}
其实CSS3中有这样一个表达式:calc()(CSS里居然有这玩意,我第一次看到时怀疑是不是SASS或LESS里的),他可以给元素的border、margin、pading、font-size和width等属性动态计算值,例如:top:calc(50% - 1rem)
,这在这种展示页面的布局中是相当有用的,上面的css可以改写成:
#cir{
width:200px;
height:200px;
border-radius:100px;
background:#888;
position:absolute;
left:calc(50% - 100px);
top:calc(50% - 100px);
}
Demo http://jsbin.com/gasato/edit
关于更多calc的介绍建议阅读大漠的这篇博文 http://www.w3cplus.com/css3/how-to-use-css3-calc-function.html
遗憾的是这玩意在桌面端浏览器中支持的已经比较好了,不过在移动端的表现令人堪忧,安卓原生浏览器从4.4开始支持,而最新版的安卓UC仍然不支持。
Introduction:
Welcome to the exciting world of crypto airdrops! Here's a curated list of active airdrops that you wouldn't want to miss. Dive in and explore the opportunities:
Share on Social Media:
🚀 Claim Your Share: Pyth Network Airdrop 🚀 Unlock the potential of $PYTH tokens in this step-by-step guide. Claim yours now! Share on Twitter
— Pyth Network (@pythnetwork) November 28, 2023
Verify Eligibility:
After sharing and connecting your walle
Active Airdrops:
Web界面构建过程中,切图是常是必不可少的一步,我认为做好这个工作需要做到:
剩下的基本就是体力活了。很多团队是没有“页面重构师”这一职位的,有时会把切图任务分配给UI设计师,而由于构建出某种样式界面的方法也多样的、并且大部分设计师没有界面开发经验,所以设计师往往不明确究竟要切出什么样的图,这一点在移动端体现的更为明显。
例如设计稿中有这样一个按钮:背景为有描边的圆角矩形,按钮上有一个icon和文字。大家在构建UI时会如何实现呢。
我想到的几种方法:
直接切出包含所有信息的一张图片
切出可伸缩的背景图,通过代码自动伸缩,然后切出icon 并打上文字
(为了能看清icon 我把透明的背景填充成了灰色)
通过代码绘制出背景、圆角、描边样式,icon采用图标字体 (关于图标字体,可以了解一下 http://iconfont.cn/ http://fontawesome.io/这两个项目)
暂且不讨论各种方法的优劣,在实际项目中三种方式都会有人使用,恐怕很多设计师是不了解UI构建中这些细节的,这就需要工程师和设计师描述清楚每一张图的需求,增加了沟通成本,切图稍有不慎会影响显示效果。
相比而言,我认为工程师自己学习一下图像工具的使用可以达到事半功倍的效果,于是便有了此文。
第一次写技术博客,受限于本人水平,有写的不如意的地方还请见谅和指正,本文适合不太会切图的工程师们阅读。
关于切图的工具,这里我们使用Photoshop(以下以Photoshop CC版本为例,不同版本可能存在一些差异),因为他更灵活、适用场景更广。
介绍一下使用PS切图时常用到的功能:
首先是图像菜单中的命令
这个好理解,就是修改图像的尺寸
需要注意的就是 一般情况下,为防止图像调整尺寸时失真,我![image]们会锁定宽高比(图中锁的按钮),另外用于屏幕上显示的图像分辨率一般设为72。想了解更多关于图像和分辨率知识 建议阅读 http://helpx.adobe.com/cn/photoshop/using/image-size-resolution.html
画布大小是图像的完全可编辑区域。好比你在一张纸上作画,发现纸张不够大时,可以通过这个选项来扩大可绘区域。
例如,下面200 * 200的图像中心有一个圆,当我把画布大小调整后,图像大小变为300 * 200,而图像中的圆的尺寸并未发生变化。你可以通过“定位”来编辑新画布的位置。
这条命令用来从图像中裁剪出所需部分。
使用方法:用选区工具从图像中选出要保留的部分,执行“图像”>“裁剪”命令。
常用于移去图像中不需要的透明区域。如下图,透明的背景上有一个球,画布的大小比球所需的区域大得多,使用“图像”>“裁切”命令可以得到非透明像素的最小区域图像。
通过勾选“顶”、“底”、“左”、“右”可以选择需要修正的区域。
该命令同样适用于非透明背景的图像,选择“左上角像素颜色”或“右下角像素颜色”可从图像中移去左上角或右下角像素颜色的区域。
再看到工具栏中切图相关的工具:
裁剪工具的使用和“图像>裁剪”命令类似。
选择裁剪工具后,可以在图像中框选出一块区域,双击后图像会移去未选中的区域。
在框选过程中可以通过工具栏中的参数来限定区域。如限定宽高、限定宽高比。
而在实际项目中,往往遇到的是一整个设计稿中有大量图片需要切出来,这就需要用到切片工具了。
选择切片工具可以在图像中划分出若干个区域,如下图:
蓝色标注的区域我我划分出的切片,灰色标注的为PS自动产生的切片。
使用可以编辑已经划分出的切片,你可以调整他的位置、大小和对齐。如需更精准的控制你的切片,可以右击该切片在“切片选项”中通过数值设置。“名称”选项既输出图片的文件名。
调整好图像,就可以开始输出了,使用“存储为 Web 和设备所用格式”命令,可以得到更优化、更适合用于构建UI的图片。执行该命令后可以对文件的格式、压缩率等参数进行配置。(想了解关于格式,推荐阅读一下这篇博文 http://ued.taobao.org/blog/2010/12/jpg_png/)如果图像中包含切片,你可以选取你所需要输出的切片,也可以选择输出用户切片或者全部切片。
我使用了来自freewebsitetemplates.com的素材用来演示一下切图,你可以从 http://www.freewebsitetemplates.com/preview/zerotype/获取到该素材。下面介绍一下我常使用的三种切图方法,仅以素材中的首页做个示范。
打开下载到的资源,可以看到里面有一个PSD文件及若干html文件,打开PSD文件,分析下,大概有这几个地方需要输出切图:
普通切图方法就是使用切片工具&&切片选择工具,在设计稿上调整好切片然后输出。
下面开始划分出我们需要的切片(注意:设计稿中已经包含原作者留下的切片,为防干扰最好使用切片选择工具把这些切片删除)。早期的浏览器对含透明通道的PNG图支持不好,所以处理含透明通道的图时我们一般使用GIF,或者索性把图形连同背景合并到一起输出不透明的图。而随着前端技术的发展和古老浏览器的淘汰,现在更好的做法是使用透明通道的PNG图片,既保证了图像质量又保证了图片在不同页面中都能适应。
要输出透明的切图,我们需要把PS中显示背景的图层隐藏。找这些填充背景的图层,点击“小眼睛”隐藏图层(图层是PS中比较重要的概念,建议阅读http://helpx.adobe.com/content/help/cn/photoshop/topics/layers.html了解更多)。
页面上这个按钮的处理,为了页面的可读性和图片能复用,我们只需要背景,所以也把文字隐藏。
现在手中的设计稿应该变成这个样子了
可以输出切图了,使用“文件>存储为 Web 和设备所用格式”命令,简单配置一下图片选项,然后选择一个路径,保存“所有用户切片”。
还没完,为了更好的体验,网页中的按钮是需要有悬停效果的,有经验的设计师早就准备好了,只不过这些效果的图层没有显示。找到并显示这个图层:
再次使用“文件>存储为 Web 和设备所用格式”命令,不过这次我们只要存这一张图就行了,在“存储为 Web 和设备所用格式”对话框中使用切片选择工具选中改切片,然后保存“选中的切片”。
可以看到,普通切图法需要手动编辑切片,耗时耗力,当设计稿元素较多时容易出错。其实在处理大量图片时,我们常用到PS的脚本功能来对图像进行批处理,切图这一工作也可以通过脚本来实现半自动化。PS中预置了一些脚本,你也可以自己录制脚本,例如有时需要把一批大尺寸的图按比例转换成小尺寸图,就可以通过脚本来实现。(想更多了解PS中脚本功能 建议阅读 http://helpx.adobe.com/content/help/cn/photoshop/topics/automation.html)
“文件>脚本>将图层导出到文件”这条命令是PS中预置的一个脚本,正如你想要的,他会把设计稿中的图层输出成图片。用这种方式切图,为了不破坏设计稿、脚本运行速度更快,我建议新建一个文档来进行。
新建一个尺寸为800*800的文档,把需要切图的元素拖动到这个文档中来,位置随意摆放,因为这个脚本会将“图层导出到文件”,所以有些元素可能包含多个图层的要使用“图层>合并图层”将他们合并。你现在的文档和图层管理器应该是这样的:
执行“文件>脚本>将图层导出到文件”
ok,在PS自动执行一系列操作后,就得到所有的切图了。
2B切图法就是使用裁剪工具,将设计稿中的每个元素分别提取出来并单独存储,仅需要输出少量图片或是要对图像局部调整时才使用此方法。
在我看来,切图本身并没有多少技术含量,但这个环节却经常出问题。经常见到设计师抱怨开发人员没能还原设计稿中的效果,其中一条重要原因就是:设计师不明确切图的所有需求,而工程师们又不会或不愿意自己切图。这就好多人共同开发产品时,前端和后端都各人自扫门前雪,这样工作是很难协作下去的。
Over!Thanks GitHub! Thanks Freewebsitetemplates!
从iOS 6+、Android 3+开始 (来源http://mobilehtml5.org/),移动端可以通过网页中的<input type="file">
来拍照上传或是上传相册中的照片。 不过从图片上传到服务器后可能会遇到图片莫名其妙旋转的问题,如图
一些设备在拍照时明明是竖着拍的(右),传到服务端后从图片查看器中看到的是横着的(左),而在一些图片处理工具或是浏览器中通过http协议看到的却是正常的,原因是在照片的exif中的Orientation属性控制了照片的旋转方向。
//Exif 信息示例
Exif.Image.Make Ascii 6 Canon
Exif.Image.Model Ascii 20 Canon PowerShot S40
Exif.Image.Orientation Short 1 top, left
Exif.Image.XResolution Rational 1 180
Exif.Image.YResolution Rational 1 180
Exif.Image.ResolutionUnit Short 1 inch
Exif.Image.DateTime Ascii 20 2003:12:14 12:01:44
Exif.Image.YCbCrPositioning Short 1 Centered
......
大部分图片查看器和编辑工具都会去根据这个属性控制照片方向,而windows自带的查看器等工具并不理睬他。
为什么要有Orientation属性呢,我并没有找到官方的解释,见到种说法挺有道理:几乎所有的摄像头在出场的时候成相芯片都是有方向的,拍出来的照片的像素都是默认方向的。如果每拍一张照片就对这些像素进行旋转,如果数码相机每秒连拍20张来算,旋转操作将会非常耗时。更聪明的做法是拍照时只记录一个方向,然后显示的时候按方向显示出来即可。因此exif定义了一个标准的方向参数,只要读图的软件都来遵守规则,加载时候读取图片方向,然后做相应的旋转即可。这样既可以达到快速成像的目的,又能达到正确的显示。
为了图片显示不出问题,我们得修改Orientation属性。首先想到的是服务端来修正方案,对于图片来说exif信息不是必须的,可以根据Orientation的值来对照片进行手动矫正的操作,然后再去掉exif,类似这样:
switch(exif['Orientation']){
case 2:
image->save;
break;
case 3:
image->rotate(-180)->save;
break;
case 4:
image->rotate(180)->save;
break;
······
}
更好的方式是用一些图片工具自动处理,GraphicsMagick和Imagemagick用来处理这个再合适不过了,他俩都可以用对图像进行旋转、裁剪、缩放、替换颜色;添加文本、水印、图形等常见操作,GraphicsMagick是从Imagemagick分支出来的,他俩有着几乎一样的API,可以在命令行工具中轻松操作。这里我用的是GraphicsMagick,他更轻便,易装易用。在Java、PHP、Nodejs等常见后端语言中可以用相关库轻松的操作GraphicsMagick的API。
后端以Nodejs为例。首先你需要在你的机器上安装GraphicsMagick。 然后npm install gm 模块就可以了。gm提供的接口非常友好,你只要
gm('/path/to/img.jpg')
.autoOrient()
.resize(240, 240)
.write('/path/to/new.jpg', function (err) {
if (err) ...
})
这样就已经完成了图片的自动修正方向和压缩尺寸的工作。
传统提交表单方式放在今天已经不能忍了,XHR2中支持把文件放在Formdata对象中异步提交,只考虑移动端,就可以舍弃iframe之类的兼容方案了。核心代码这样:
var xhr = new XMLHttpRequest();
var formData = new FormData();
formData.append('file', input.files[0]);
xhr.open('POST', form.action);
xhr.send(formData);
XHR2中还可以通过process事件来监听进度,实现类似进度条的功能
xhr.onprogress = updateProgress;
xhr.upload.onprogress = updateProgress;
function updateProgress(event) {
if (event.lengthComputable) {
var percentComplete = event.loaded / event.total;
......
}
}
用FormData发送的请求头中你的Content-Type 会变成这样 multipart/form-data; boundary=----WebKitFormBoundaryyqVkWF3PcCpAzZp9,如果上传时要附带参数也可以直接append到formData里。
然后Nodejs中可以用connect-busboy来接收文件,在express框架中大概是这样:
var express = require('express'),
http = require('http'),
fs = require('fs'),
busboy = require('connect-busboy'),
gm = require('gm');
var app = express();
app.use(busboy());
.....
app.post('/upload', function(req, res) {
req.busboy.on('file', function(fieldname, file, filename, encoding, mimetype) {
......
file.on('end', function () {
gm(filePath)
.autoOrient()
.thumbnail(200, 200)
.write(fullname, function(err){
if (err) return console.dir(arguments)
res.json({
......
});
}
)
});
file.pipe(fs.createWriteStream(filePath));
});
req.pipe(req.busboy);
});
......
app.listen(3001);
图片上传的主体工作算是完成了,不过现在手机随便拍张照片就是一两兆,wifi环境下不说,移动网络通过这方案上传照片就有点坑了。手机客户端中一般会先压缩图片再上传,Web中如何实现压缩后上传呢?
可以把图片读到canvas中,然后用canvas.toDataURL()接口输出画布的base64编码,再把base64编码转成Blob塞到Formdata里传到后端。这样即可以压缩图片减少流量,又可以在前端就修正图片旋转的问题。(Discuss:直接把base64传到后端是否可行呢,后面试一试)
canvasResize这个库已经把一切封装好了https://github.com/gokercebeci/canvasResize ,同时他依赖Exif.js 修正了因Orientation属性产生的旋转问题。前端主要的代码:
var file = input.files[0];
canvasResize(file, {
width: 300,
height: 0,
crop: false,
quality: 100,
callback: function(data, width, height) {
var blob = canvasResize('dataURLtoBlob', data);
var form = new FormData();
form.append('file',blob);
$.ajax({
type: 'POST',
url: server,
data: form,
contentType: false,
processData: false,
}).done(function (res) {
......
}).fail(function () {
......
}).always(function () {
......
});
}
});
Nodejs中代码可以参考前面的,继续用connect-busboy模块接收文件。
实际测试一下iOS没问题,Android 4 有些机型不行,貌似修改过file的Blob数据发到服务端的数据字节就会为0 这是安卓的bug https://code.google.com/p/android/issues/detail?id=39882 。 网上有人给出的解决方案是用FileReader把文件读出来,然后把整个二进制文件当请求发到服务端,这种方式要附带参数的话只能放url里了。
var reader = new FileReader();
reader.onload = function() {
$.ajax({
type: 'POST',
url: server,
data: this.result,
contentType: false,
processData: false,
beforeSend: function (xhr) {
xhr.overrideMimeType('application/octet-stream');
},
}).done(function (res) {
......
}).fail(function () {
......
}).always(function () {
......
});
};
reader.readAsArrayBuffer(file);
后端在接收这些数据时,会是一段一段的,我是用的拼接的方式处理
app.post('/upload', function(req, res) {
var imagedata = '';
req.setEncoding('binary');
req.on('data', function (chunk) {
imagedata += chunk
});
req.on('end', function (chunk) {
fs.writeFile(filePath, imagedata, 'binary', function(err){
if (err) throw err
res.json({
......
});
})
});
});
实测一下,稍低端的的安卓上有点卡,毕竟处理一张图片的运算量可不小,目测目前用前端压缩上传方案的不多,至少微博触屏版 (http://m.weibo.cn/) 就是把原始图片直接上传的,这种方式是否适合直接使用或者还有哪些可以优化的地方有待验证。
这里有一个完整的demo https://github.com/xiangpaopao/mobile-upload-demo 包括上面提到的两种方案
使用的话,依次安装GraphicsMagick > npm install > node app.js
做移动端页面开发时,时常想获取一下浏览器尺寸啥的,一直对移动端screen接口把握不准,网上能看到的资料也不多,打算把这些内容整理了一下。
首先回顾下在桌面浏览器中用到的接口,这里只是列出,具体了解建议阅读 CSSOM视图模式(CSSOM View Module)相关整理 与 w3c关于Cssom的文档翻译
window.screenX
window.screenY
window.scrollX
window.scrollY
window.screenLeft
window.screenTop
window.innerHeight
window.innerWidth
window.outerHeight
window.outerWidth
window.pageXOffset
window.pageYOffset
screen.height
screen.width
screen.availHeight
screen.availWidth
screen.availLeft
screen.availTop
document.body.clientTop
document.body.clientLeft
document.body.clientWidth
document.body.clientHeight
document.body.scrollWidth
document.body.scrollHeight
document.body.scrollTop
document.body.scrollLeft
document.body.offsetWidth
document.body.offsetHeight
document.body.offsetParent
做过移动网页开发的对viewport这个概念不会陌生,他最早是Mobile Safari提出来的,用来把页面放在一个虚拟的“窗口”(viewport)中,通常这个虚拟的“窗口”(viewport)比屏幕宽,这样就不用把每个网页挤到很小的窗口中。要了解更多可以阅读Mozilla 开发者博客上的 在移动浏览器中使用viewport元标签控制布局
提到viewport自然要提到devicePixelRatio这个概念,即设备像素比(设备上物理像素与设备独立像素的比例),如devicePixelRatio为2时,就表示用4个物理像素来显示一个页面像素。在浏览器中可以用window.devicePixelRatio获取到设备像素比,早期显示器的设备像素比都为1,后来随着手机屏幕分辨率的提升为了不改变浏览阅读体验移动设备就诞生了设备像素比这个概念。这个比值现在可能的值有很多1、1.5、2、3 ... 并且一些其他设备如MacBook也开始使用Retina屏了。一些设备的屏幕参数可以参考 http://screensiz.es/
由于这些接口有些不在标准中,各浏览器的理解与实现也不尽相同,加上移动设备屏幕的局限性,所以一些接口在移动端上并没有多少意义。为此我用了几台机器做了如下测试,测试用的 demo地址
可以看到 这些接口在iOS中的浏览器几乎没有兼容性问题,而安卓上的表现不太靠谱。
总结下,移动端屏幕/窗口接口中唯独靠谱的就是 window.innerHeight/window.innerWidth 了,好在还有这个可以用...
原文地址:http://davidwalsh.name/css-js-animation
基于JavaScript的动画性能竟然达到甚至超过了CSS transtions动画? Adobe和Google不断发布web App是打算挑战native App?
本文一步一步的诠释了基于JavaScript DOM的动画库(例如Velocity.js与GSAP)为何会比基于jQuery或CSS的动画库有更好的性能。
首先,不要把JavaScript和jQuery错误的混为一谈。 jQuery展示动画会比原生JavaScript慢。 原因是尽管jQuery非常强大,但是他的目标从来不是成为一个高性能的动画引擎。
小节:布局抖动发生在动画初始阶段,动画运行阶段js的垃圾回收会造成卡顿,而用动画帧频低是因为没有使用RAF。
如何避免由DOM查询和DOM更新而引起的布局抖动。
var currentTop,
currentLeft;
/* 产生布局抖动 */
currentTop = element.style.top; /* 查询 */
element.style.top = currentTop + 1; /* 更新 */
currentLeft = element.style.left; /* 查询 */
element.style.left = currentLeft + 1; /* 更新 */
/* 不产生布局抖动 */
currentTop = element.style.top; /* 查询 */
currentLeft = element.style.left; /* 更新 */
element.style.top = currentTop + 1; /* 更新 */
element.style.left = currentLeft + 1; /* 更新 */
查询会迫使浏览器在更新完页面后重新计算页面样式(同时考虑到新的更新的影响)。 由此产生的开销对动画运行的影响至少是16ms。
使用RAF接口并不需对现有的库进行重构。 让我们来比较一下RAF与setInterval的基础用法:
var startingTop = 0;
/* setInterval:为了达到60fps把间隔设为16ms (1000ms/60 ~= 16ms). */
setInterval(function() {
/* 因为此处每秒执行60次,所以我们将top属性的增量设置为1/60秒 */
element.style.top = (startingTop += 1/60);
}, 16);
/* requestAnimationFrame: 当浏览器处于正常时,尝试以60fps运行 */
function tick () {
element.style.top = (startingTop += 1/60);
}
window.requestAnimationFrame(tick);
结论:使用RFA会以很少的代码改动量获取更高的动画性能
CSS Transitions超越了jQuery卸载动画逻辑到浏览器本身,这有效地
1.通过优化DOM操作和内存消耗来避免卡顿;
2.使用底层的RAF机制
3.强制硬件加速(利用GPU的能力来提高动画表现)。
然而事实上,JavaScript本身也能实现这些优化。 并且GSAP一直就是这么做的。 新型动画引擎Velocity.js,在现有的技术上又进一步优化,这个我们待会讨论。
由此可见,通过对程序的改进,Javascript动画是可以和CSS动画相提并论的。 并且JavaScript动画实际上可以比他们快。
让我们思考一下CSS动画库的缺点
相反:基于JavaScript动画库可以自己决定何时启用硬件加速,他们支持各版本的IE,并且他们非常适合批量优化动画。
我建议只有当你是为了移动开发并且动画只包含简单的状态变化时才使用原始的CSS transitions。 在这种情况下,transitions是一个高性能的原生解决方案,允许你保留所有动画逻辑在样式表内,从而避免使用JavaScript库。 然而,如果你在设计复杂UI或正在开发一个有多种状态UI的应用程序,总得使用一个动画库来确保你的动画性能和工作流程仍是可控的。有一个管理CSS transitions很棒的库 Transit。
好吧,既然说JavaScript性能可以占据上风,那么到底快多少呢? 快到足够创建一个能与WebGL相媲美的3D动画示例;快到足够创建一个你认为要使用Flash或者After Effects才能制作出来的超炫片头。快到足够创建一个你以为canvas才能实现的虚拟世界。
下面比较一下主流动画库的性能,包括Transit(使用了CSS transitions),在Velocity文档的顶部 VelocityJS.org 。
问题来了:通过什么标准来判断JavaScript的性能呢? 下面列举了基于Javascript的动画可以优化的要素:
回顾下我们之前讨论的关于布局的问题,Velocity.js利用这些最佳实践来缓存重用一个动画的最终值的起始值的动画,从而避免一开始就查询DOM元素的值:
$element
/* 将元素滑到视图的下面 */
.velocity({ opacity: 1, top: "50%" })
/* 延迟1000ms后将元素滑到到视图之外 */
.velocity({ opacity: 0, top: "-50%" }, { delay: 1000 });
在上面的例子中,第二此调用Velocity时自动使用opacity:1与top:50%作为初始值。
浏览器可能最终会对一些相同的事情进行优化,但这样做会减少动画代码量以方便开发人员。 因此,出于同样的原因,jQuery不使用RAF(见上),浏览器不会为了优化而对这种机制有任何干扰。
最后,让我们来比较一下这两个JavaScript动画库(Velocity.js和GSAP)。
我的建议当你需要精确控制时间(如重新映射、暂停/恢复/寻求)、动作(如bezier曲线路径),或复杂的分组/排序时使用GSAP。 这些特性对于游戏开发和某些领域的应用程序至关重要,但是对于web应用程序的UI就不那么重要了。
GSAP的功能丰富并不意味着Velocity自身的功能弱。 相反,Velocity不仅包含了jQuery的$.animate()的所有功能 ,而且还集成了颜色动画, transforms, 循环, easings, 类动画, 滚动动画。所有的这一切,压缩后仅仅只有7KB
简而言之,Velocity相当于是把jQuery,jQuery UI,和CSS transitions结合了起来。
此外,从使用的角度来看,Velocity使用了jQuery的$.queue()方法,从而与jQuery的$.animate() ,
先让我们来初步了解一下 Velocity.js 与 $.animate()的相同点 :
$element
.delay(1000)
/* 用Velocity控制元素的向上运动持续2000ms */
.velocity({ top: "50%" }, 2000)
/* 当元素向上运动的动作完成后 使用jQuery自带的方法使元素淡出 */
.fadeOut(1000);
进一步,可以创建一个3D滚动的动画场景也只是两个简单的代码:
$element
/* 在1000ms内将浏览器滚动到元素的上方 */
.velocity("scroll", 1000)
/* 然后使元素沿着他的Y轴旋转360度 */
.velocity({ rotateY: "360deg" }, 1000);
Velocity的理念是在DOM动画方面高性能、简单易用。本文侧重点是前者,在 VelocityJS.org 的头部可以更多地了解后者。
在我们结束之前,记住这一点:一个高性能的UI不仅仅需要选择正确的动画库,更重要的是优化你的页面。 你可以总谷歌的分享会中学到更多关于这些:
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.