再议viewport

viewport都在用,但详细分析的文章并不多。苹果官方的开发文档有一些介绍,但语焉不详,且和实际情况有不少出入。本文在iPhone4,iOS 5.01的环境下测试viewport在各种参数下的具体表现,供日后参考。错漏之处,敬请指正。

首先看一下官网说的几点结论(红色部分与实际情况有出入):
1、iOS的safari下viewport默认宽度为980px(btw:Opera 850px, Android 800px, IE 974px)。

Default width and initial scale

2、最常用的width = device-width,其值实际为320px(除ipad外的所有iOS设备)。

3、如果只设置initial-scale = 1.0,则竖屏下为device-width(320px),横屏下为device-height(480px)。

http://developer.apple.com/library/safari/documentation/AppleApplications/Reference/SafariWebContent/Art/textat980withinitialscale1_0.jpg

4、如果只设置width = 320,则不论横竖屏宽度都会自动缩放到320px。

Width set to 320 with default initial scale

5、viewport最低宽度为200px。

Width set to 200 with default initial scale

6、同时设置width = 980 和initial-scale = 1.0。原文是这样说的:“Safari on iOS infers the height by maintaining a ratio equivalent to the ratio of the visible area in either orientation. Therefore, if the width is set to 980 and the initial scale is set to 1.0 on iPhone, the height is set to 1091 in portrait and 425 in landscape orientation.”恕我愚钝,实在不理解这个1091和425是根据怎样的一个ratio计算出来的。也实在看不懂下面两副示例图如何跟980,1091和425有半毛钱关系。望得道之人予以指点。

Width set to 980 and initial scale set to 1.0

—————————————————————————————————————

下面开始验证。无意间google到几个专门测量viewport的页面:
http://www.adrianworlddesign.com/Viewport-Demo/metatag
http://philarcher.org/cgi-bin/doctype.pl

两个页面都可以自由设置viewport的参数,遗憾的是前一个页面的doctype在我的iphone上似乎不太兼容,viewport设置不能生效,而后者并没有提供任何直观测量页面宽度的元素。还是自己动手吧:

< !DOCTYPE HTML>
<html>
<head>
    <meta name="viewport" content="initial-scale=1" />
	<title>Viewport Test</title>
	<style>
	*{
	margin:0;
	padding:0;
	}
	body>div{
	font-size:48px;
	height:80px;
	margin-bottom:80px;
	font-weight:bold;
	text-align:center;
	}
	.w980{
	background:#EB8F00;
	width:980px;
	}
	.w640{
	background:#2E6E9E;
	width:640px;
	}
	.w320{
	background:#4CA20B;
	width:320px;
	}
	</style>
</head>
<body>
	<script>
		var width = window.innerWidth;
		document.write('<div style="border:1px solid #000;width:'+(width-2)+'px">'+width+'px</div>');
	</script>
	<div class="w980">980px</div>
	<div class="w640">640px</div>
	<div class="w320">320px</div>
</body>
</html>

测试使用adobe shadow,这实际上是一个免费的weinre的GUI工具,非常方便调试移动页面。使用方法也很简单,就是手机上装一个app,然后在pc上运行一个监听程序,chrome上装一个扩展,配对后即可自动同步显示页面并实时inspect。官方视频教程在此,一看就会。

—————————————————————————————————————

Test Case 1:只设置initial-scale

initial-scale 横屏 竖屏
0.5 640px 640px
1 320px 320px
2 160px 160px
3 107px 107px

结论显而易见:无论页面宽高,width始终为device-width/X。与官方说法并不一致(The viewport width is set to device-width in portrait orientation and device-height in landscape orientation.)

—————————————————————————————————————

Test Case 2:只设置width
列表测试的过程中很容易发现一个问题:只设置width时viewport的实际宽度还会受页面尺寸的影响!
当页面不超宽或超宽不超高时,宽度尚能维持在设定的width值,否则viewport的宽度将按比例缩小到垂直无需滚动或为980px为止,且此时screen.width/screen.availWidth/window.outerWidth/window.innerWidth/document.body.clientWidt等都无法反映正确的可视宽度。
为了简化测试,我们去掉供对比用的三个div元素,使以下测试结果皆在页面不超高的前提下得到。

viewport宽度 横屏 竖屏
200 200 200
device-width/320 320 320
480 480 480
640 640 640

—————————————————————————————————————

Test Case 3:同时设置width和initial-scale

width/initial-scale 横屏 竖屏
320/1 320 320
640/1 640 640
320/2 160 160
640/2 160 160

结论同样单纯无比:只有initial-scale有效

—————————————————————————————————————
基于上述结论,我们需要两个准确的解决方案。千变万化的布局,放到手机上其实只有两种:
1、缩放自适应
2、响应式布局

第一种,缩放自适应的页面, 可以使用

<!--最普通最常见的用法-->
<meta name="viewport" content="width=device-width" />
<!-- 如果有足够的高清图资源,主要支持retina设备(ip4/4s),那么推荐下面这种-->
<meta name="viewport" content="width=640" />
<!-- 如果针对new iPad,甚至可以试试这个-->
<meta name="viewport" content="width=1536" />

第二种,复杂的响应式布局,实际就是要求页面在切换横屏时不放大,从而留出空白用以填充或调整列布局。
此时可以看到无论width还是initial-scale都无法限制横屏时的放大,因而必须用上minimum/maximum-scale,而width和initial-scale则推荐使用initial-scale,使三个scale参数统一。

<!--最普通最常见的用法-->
<meta name="viewport" content="initial-scale=1,maximum-scale=1,minimum-scale=1" />
<!--for retina screen-->
<meta name="viewport" content="initial-scale=0.5,maximum-scale=0.5,minimum-scale=0.5" />

[译]CSS3的关键帧动画(Keyframe Animations)简介

CSS3的关键帧动画(Keyframe Animations)简介
原文:http://coding.smashingmagazine.com/2011/05/17/an-introduction-to-css3-keyframe-animations/
作者:Louis Lazaris
译者sunnylqm,转发请注明。

你现在可能已经听说过基于关键帧语法的CSS3动画了。CSS3规范中的动画模块已经发布了几年了,而它也越来越显示出在WEB设计中扮演重要角色的巨大潜力。
开发者可以使用CSS3关键帧动画创建平滑,性能优异且易于维护的动画,而并不需要大量的脚本代码。这无疑是使用CSS3解决现实问题的又一种优雅的方案。如果你还没有开始学习CSS3动画的语法,那么就在CSS3规范正式完成之前开始充电吧。
本文中,我们将讲解所有重要的语法,并且将列出现有的浏览器支持情况,以让你确认什么时候可以部署这种技术。
[编者注:推荐一本专业web设计者和开发者必备的书:The Printed Smashing Books Bundle,有很多日常工作中实用的见解。]

一副简单的风景动画

为方便本文说明,我创建了一个简单的风景动画来介绍这种语法的各个方面。你可以先访问一下这个demo来大致了解一下我要描述的东西。在这个页面上有一个用来显示各种元素(太阳,月亮,天空,大地和云彩)的CSS代码的侧边栏。先快速浏览一下,然后在我描述CSS3动画模块的不同部分时回头研究。
(注意:Safari 5.1之前的版本有一个bug使得动画不能正确的结束。详细信息请参阅下文的“动画填充模式”一节。)
我将只描述“太阳”元素相关的CSS,而这应该已经足够帮助你理解基于关键帧的动画的原理了。你可以点击demo页面上的侧边栏的标签页,去查看和研究其他元素的相关代码。

@keyframes的语法

CSS3动画代码的第一个值得注意的不寻常之处,就是使用@keyframes语法。根据规范,在这种特定的CSS语法中,@keyframes后应该紧跟一个标识符(由开发者自定),此标识符将在其他CSS代码中引用。
在@keyframes和标识符之后,就是一系列的动画规则(就像普通的CSS代码中声明的style规则)了。这一系列动画规则用大括号括起来隔开,然后再嵌在@keyframes之后的大括号里,类似其他@语法规则。
以下即是我们要使用的@语法:

@keyframes sunrise {
    /* 这里是动画规则 … */
}

sunrise是我们选来引用这个动画的标识符。
请注意在示例代码中我并没有使用任何私有前缀。在文章的最后我会列出浏览器的支持情况,但截至发文时,尚无浏览器对标准语法提供支持,所以在实践中,你还是得加上私有前缀。

Keyframe选择器

让我们在@keyframes中添加一些动画规则:

@keyframes sunrise {
   0% {
      bottom: 0;
      left: 340px;
      background: #f00;
   }

   33% {
      bottom: 340px;
      left: 340px;
      background: #ffd630;
   }

   66% {
      bottom: 340px;
      left: 40px;
      background: #ffd630;
   }

   100% {
      bottom: 0;
      left: 40px;
      background: #f00;
   }
}

通过添加这些新的动画规则,我们引入了keyframe选择器。在上述示例代码中,0%, 33%, 66%, 以及100%即为keyframe选择器。其中,0%和100%可以使用”from”和”to”来代替。
示例中的四套动画规则表达的是这个动画元素的四种状态(四个关键帧),以及处于这四种状态中时的样式。那些没有定义的状态(比如,从34%到65%)则组成了这些已定义的状态间的过渡状态。
尽管规范还在修改中,有一些规则用户还是应该遵守。例如,keyframes的书写顺序并不重要,它们会按百分数的升序播放。因此,如果你把”to”关键帧放在”from”关键帧之前,动画的播放并不会有改变。除此以外,如果你没有指定to或者from或者对应的百分数,浏览器会自动加上。所以,@keyframes的语法并不符合一般CSS语法的层叠覆盖规则。

太阳动画中使用的Keyframes

为了让demo中的太阳动起来,我设置了四个关键帧。前文中已经列出了这些代码,我将进一步用注释描述其中变化。
在起始关键帧中,太阳是红色的(就像刚升起或者即将落下时),而且它的位置在“地平线”以下,不可见。为了使left和bottom值生效,需要给太阳元素绝对或者相对定位。我也使用了z-index来将元素叠起来 (比如,确保地平线在太阳之上)。请注意,keyframes中只写出那些需要变化的样式。其他样式(比如z-index和position这样的固定值)写在样式表的其他位置。

10% {
    bottom: 0; /* 太阳靠近底部 */
    left: 340px; /* 太阳靠近右侧 */
    background: #f00; /* 太阳是红色的 */
}

大概三分之一的动画时间 (33%),太阳都在同一垂直线上缓缓升起,并逐渐变为橙黄色。

33% {
    bottom: 340px; /* 太阳升起 */
    left: 340px;
    background: #ffd630; /* 变色 */
}

然后,大概三分之二的动画时间 (66%),太阳往左移动了大概300个像素,但保持在同一水平线上。注意在66%的关键帧中还有一些东西:我仍然保留了33%关键帧中的颜色值,以使太阳不至于过早变回红色。

66% {
 bottom: 340px;
 left: 40px; /* 太阳向左滑过天空 */
 background: #ffd630; /* 保持原先的颜色 */
}

最后,太阳逐渐走向最终阶段(大红色),消失在地平线。

100% {
 bottom: 0; /* 日落 */
 left: 40px;
 background: #f00; /* 变回红色 */
}

将动画名与元素相关联

下面这段代码将动画名(在本例中,即为sunrise)与我们的HTML中的特定元素关联了起来:

#sun.animate {
 animation-name: sunrise;
}

现在我们介绍animation-name属性。这个属性值必须和现有的@keyframes标识符相匹配,否则无法执行动画。某些情况下你可以使用脚本将其置为none(这个属性唯一的保留字)来阻止动画执行。
我们的目标对象是id为sun,class为animate的元素。同时使用id和class的目的是方便使用脚本来动态添加class。在这个demo中,页面起始状态是静止的。然后,在点击开始按钮后,所有相关元素都会被加上animate的class,从而触发动画的执行并允许用户来控制播放。
当然,除此以外还有很多别的方法。

动画持续时间和计时函数

我们添加两行代码:

#sun.animate {
 animation-name: sunrise;
 animation-duration: 10s;
 animation-timing-function: ease;
}

你可以使用animation-duration来指定动画的持续时间。持续时间定义的是单次动画循环所需的时间。单位可以是秒或者毫秒(4s,2000ms等),甚至带小数的秒数(3.3s)。
规范中并没有指定计时单位。然而,使用比秒长的计时单位似乎也并无必要,更何况你完全可以通过计算将分钟、小时和日期转为秒数甚至毫秒数。
animation-timing-function属性如果用于整个动画中,则定义了动画的每次循环是如何随时间递进的。 animation-timing-function的可选值有ease, linear, ease-out, step-start以及其他在规范中所列的
本例中使用的是ease,同时也是默认值。所以在这里即便去掉这一属性,动画的执行也一样。
此外,对于每一个关键帧都可以像这样指定计时函数:

@keyframes sunrise {
 0% {
 background: #f00;
 left: 340px;
 bottom: 0;
 animation-timing-function: ease;
 }

 33% {
 bottom: 340px;
 left: 340px;
 background: #ffd630;
 animation-timing-function: linear;
 }

 66% {
 left: 40px;
 bottom: 340px;
 background: #ffd630;
 animation-timing-function: steps(4);
 }

 100% {
 bottom: 0;
 left: 40px;
 background: #f00;
 animation-timing-function: linear;
 }
}

以上各个关键帧都有单独的计时函数。其中有一个是steps,这将使动画按指定的步骤数跳跃式进行。结尾关键帧(100%或者”to”)也有其自己的计时函数,但由于它是正序播放的动画的结尾关键帧,其计时函数只有在倒序播放时才会生效。
在我们的例子中并没有为每个关键帧设定单独的计时函数,但以上解释已经足以说明这是可行的。

动画循环次数和方向

…再加两行:

#sun.animate {
 animation-name: sunrise;
 animation-duration: 10s;
 animation-timing-function: ease;
 animation-iteration-count: 1;
 animation-direction: normal;
}

这里又引入两个新的属性:一个告知动画的循环次数,一个告知浏览器是否按正序循环动画。
animation-iteration-count属性值被设为1,也就是只播放一次。此属性接受整数值或者infinite。
此外,animation-direction被设为normal(同时也是默认值),意味着每次播放都是同样的顺序(从头到尾正序播放)。在我们的例子中,动画被设置为只播放一次,所以这个属性是不必要的。其他的可选值有alternate,可以使得动画每次按不同的顺序来回播放。当然,为使alternate生效,循环次数需要大于等于2次。

动画的延迟和播放状态

…继续添加两行代码:

#sun.animate {
 animation-name: sunrise;
 animation-duration: 10s;
 animation-timing-function: ease;
 animation-iteration-count: 1;
 animation-direction: normal;
 animation-delay: 5s;
 animation-play-state: running;
}

我们先介绍animation-delay属性。顾名思义,它允许动画延迟指定一段时间后执行。有趣的是,这个属性可以使用负值。当使用负值时,动画会从中间指定的某个时间开始。
animation-play-state属性有可能从规范中被移除。它一共接受两个值:running和paused。此属性比较废。默认值为running,而paused则将动画标记为暂停状态。说它废是因为你并不能在CSS中为某个keyframe指定paused状态。唯一有用的场合就是某些场景中可以使用脚本修改这个值来响应用户的操作。

动画填充模式

我们最后再添加一行代码,用于定义“填充模式”:

#sun.animate {
 animation-name: sunrise;
 animation-duration: 10s;
 animation-timing-function: ease;
 animation-iteration-count: 1;
 animation-direction: normal;
 animation-delay: 5s;
 animation-play-state: running;
 animation-fill-mode: forwards;
}

animation-fill-mode属性允许你定义动画元素的样式在动画执行前或执行后生效。backwards指定起始帧在动画开始前就提前应用其样式,而forwards使得结尾帧在动画结束后才应用其样式。使用both值可以使得两种情况同时生效。
UPDATE:animation-fill-mode并没有出现在最新的规范草案中,但在editors draft中能找到。同时,在某些版本的Safari中(5.0及更老版本),只有定义了两个关键帧的动画可以正常触发forwards值。这些版本的浏览器似乎始终将第二个关键帧视为”forwards”起作用的对象,其他浏览器中则无此问题。正确的行为应该是仅仅将forward值应用在结尾帧上。Safari 5.1修正了这个问题。

简写

最后,规范中描述了动画的简写方式,可以同时组合上述六个属性,但不包含animation-play-state和animation-fill-mode。

关于DEMO和浏览器支持的一些说明

正如之前所说的,本文只对demo中的“太阳”元素进行了讲解。你可以在demo页面中查看完整的代码。
这个demo并没有使用任何图片,而且动画过程本身也不依赖javascript。太阳,月亮和云等元素都完全是由CSS3的border-radius创建的, 而唯一用到脚本的地方就是右边的tab页和开启/重置动画的按钮。
以下是支持CSS3关键帧动画的浏览器:

* Chrome 2+,
* Safari 4+,
* Firefox 5+,
* IE10 PP3,
* iOS Safari 3.2+,
* Android 2.1+.

尽管官方并没有宣布,但opera未来还是有望提供支持
如果在编写动画时只使用了一种私有前缀,你可以借助类似PrefixrAnimation Fill Code这样的工具来为你自动填充其他的私有前缀。

扩展阅读

* “CSS Animations Module Level 3,” W3C
* “CSS3 Animations,” Robert Nyman
* “CSS3 Animator,” Anthony Calzadilla
A website “dedicated solely to CSS3/JavaScript animation and how to realistically use it in your projects today.”
* “Adding Delight to Your Design with CSS3 and Webkit Keyframe Animation,” Jason Garber
* “Using CSS3 Transitions, Transforms and Animation” (with examples), Rich Bradshaw
* “Creating Awesome CSS3 Animations,” by Thomas Fuchs

[译]跨浏览器的多点触控与鼠标事件处理

原文地址:
http://blogs.msdn.com/b/ie/archive/2011/10/19/handling-multi-touch-and-mouse-input-in-all-browsers.aspx
原作者Ted Johnson, Graphics Program Manager Lead, Internet Explorer
译者sunnylqm,转发请注明。

本文主要解释了Web开发者如何通过使用IE10中新引入的pointer事件模型、iOS上的touch事件模型以及W3C标准的扩展鼠标事件模型来编写普适的跨浏览器的触控事件处理代码。

题外话:我很幸运的拥有一台三爽的700T开发者平板。通过它可以很好的体验IE Test Drive中的多点触控demosTouch EffectsLasso Birds。你可能也注意到了,Lasso Birds在除了IE10以外的设备和浏览器上也能很好的运行,比如,它的多点触控在iOS设备上也能运作。本文中,我以Lasso Birds的一些代码模式为例,将其普适拓展至兼容比较老的浏览器。
原文有附带一个触控绘图的demo,以下代码将基于这个demo讲解。)

代码部分

用鼠标来绘图的基本流程简单明了:

var drawingStarted = false;
function DoEvent(eventObject) {
	if (eventObject.type == "mousedown") {
		drawingStarted = true;
		startDraw(eventObject.pageX, eventObject.pageY);
	}
	else if (eventObject.type == "mousemove") {
		if (drawingStarted) {
			extendDraw(eventObject.pageX, eventObject.pageY);
		}
	}
	else if (eventObject.type == "mouseup") {
		drawingStarted = false;
		endDraw();
	}
}

唯一需要改动以支持IE10的pointer事件的地方就是得注意多点触控可能同时发生(多个点由不同的pointerId区分)。IE10的pointer模型会触发每个触点的单独的MSPointerDown, MSPointerMove和MSPointerUp事件。

var drawingStarted = {};
function DoEvent(eventObject) {
	eventObject.preventManipulation(); // 如果不加上这句, 则屏幕的拖动会代替绘图的动作
	var pointerId = eventObject.pointerId;
	if (eventObject.type == "MSPointerDown") {
		drawingStarted[pointerId] = true;
		startDraw(pointerId, eventObject.pageX, eventObject.pageY);
	}
	else if (eventObject.type == "MSPointerMove") {
		if (drawingStarted[pointerId]) {
		extendDraw(pointerId, eventObject.pageX, eventObject.pageY);
		}
	}
	else if (eventObject.type == "MSPointerUp") {
		delete drawingStarted[pointerId];
		endDraw(pointerId);
	}
}

要兼容Apple的iOS的touch事件模型则需要你遍历每一个 touchstart, touchmove, 和touchend事件中的changedTouches。因为在iOS事件模型中,同一个事件中可能包含同时产生的不同状态的触控点。像IE10的pointer模型一样,我们也需要用唯一的id来区分不同的触控点。

var drawingStarted = {};
function DoEvent(eventObject) {
	eventObject.preventDefault(); // 如果不加上这句, 则屏幕的拖动会代替绘图的动作
	for (var i = 0; i < eventObject.changedTouches.length; ++i) {
		var touchPoint = eventObject.changedTouches[i];
		var touchPointId = touchPoint.identifier;
		if (eventObject.type == "touchstart") {
			drawingStarted[touchPointId] = true;
			startDraw(touchPointId, touchPoint.pageX, touchPoint.pageY);
		}
		else if (eventObject.type == "touchmove") {
			if (drawingStarted[touchPointId]) {
				extendDraw(touchPointId, touchPoint.pageX, touchPoint.pageY);
			}
		}
		else if (eventObject.type == "touchend") {
			delete drawingStarted[touchPointId];
			endDraw(touchPointId);
		}
	}
}

合并以上三种代码需要注意事件名称和触控点id名称的区别,以及鼠标事件模型中并没有触控点id。
在以下合并后的代码中,我也加入了对”move”事件是否实际发生的检查。这是因为在IE10的pointer模型中,如果一个触控点始终被压下但没有移动,即便产生完全相同的x和y坐标,MSPointerMove事件也会被持续触发。通过过滤这些冗余的“移动”,可以消除这些对extendDraw()的无谓调用。我是这样实现这个检查的:将上一次的start或move事件中的xy坐标存储到一个lastXY的对象中,然后在后续的事件中检查这个lastXY。lastXY此时代替了前两段示例代码中的drawingStarted对象。

var lastXY = { };
function DoEvent(eventObject) {
	// 阻止拖动和缩放使得绘图能够正常进行
	if (eventObject.preventManipulation)
		eventObject.preventManipulation();
	else
		eventObject.preventDefault();

	// 如果存在changedTouches数组,则使用它,否则使用eventObject创建一组
	var touchPoints = (typeof eventObject.changedTouches != 'undefined') ? eventObject.changedTouches : [eventObject];
	for (var i = 0; i < touchPoints.length; ++i) {
		var touchPoint = touchPoints[i];
		// 获取唯一的touchPoint id,如果不存在,则使用1作为默认值
		var touchPointId = (typeof touchPoint.identifier != 'undefined') ? touchPoint.identifier : (typeof touchPoint.pointerId != 'undefined') ? touchPoint.pointerId : 1;

		if (eventObject.type.match(/(down|start)$/i)) {
			// 处理mousedown, MSPointerDown,以及touchstart事件
			lastXY[touchPointId] = { x: touchPoint.pageX, y: touchPoint.pageY };
			startDraw(touchPointId, touchPoint.pageX, touchPoint.pageY);
		}
		else if (eventObject.type.match(/move$/i)) {
			// 处理mousemove, MSPointerMove,以及touchmove事件
			if (lastXY[touchPointId] &#038;& !(lastXY[touchPointId].x == touchPoint.pageX &#038;& lastXY[touchPointId].y == touchPoint.pageY)) {
				lastXY[touchPointId] = { x: touchPoint.pageX, y: touchPoint.pageY };
				extendDraw(touchPointId, touchPoint.pageX, touchPoint.pageY);
			}
		}
		else if (eventObject.type.match(/(up|end)$/i)) {
			// 处理mouseup, MSPointerUp,以及touchend事件
			delete lastXY[touchPointId];
			endDraw(touchPointId);
		}
	}
}

上面这个例子略去了注册和接受事件以及确保它们被应用在绘图目标上的代码。要使这些代码真正能在所有的浏览器——包括IE9之前的浏览器上跑起来,则还需要一些额外的工作。感兴趣的朋友可以阅读这个跨浏览器的多点触控绘图类的最终代码
通过同时为鼠标和触控设备编码,web开发者可以确保他们的站点工作在所有的浏览器上——无论是PC,平板还是手机。

如何在retina设备上使用高清图

讨论这个问题之前,先讨论下为什么iPhone4/new iPad的页面上的图片会不清晰.
情况一:
众所周知,iPhone4(以及4s,new iPad)的屏幕宽度为640px——这个称为设备像素(Device Pixel,以下简称DP),是之前的iPhone的2倍(320px).
情况二:
无论是320px还是640px,用来浏览平常的网页,其宽度都远远不够,因而手机浏览器中通常会有一个viewport的概念.
在默认的viewport中,iPhone的屏幕宽度被拉伸到980px——这个称为CSS像素 (以下简称CP),这样几乎所有的页面都可以在iphone的浏览器中完全展示而不用水平滚动。当然,其代价是页面拥挤不堪,可读性极差.
情况三:
对于专门适配iPhone的页面,首先就需要修改默认的viewport,在页面head中加入这样一句:

<meta name="viewport" content="width=device-width"/>

那么device-width是多少呢? 320px . 而且,所有的iPhone都一样,都是320px. 显然,这是为了向后兼容.

因而问题就来了,在iPhone4中,DP为640px,CP却只有一半,一张100px*100px的图,由于每1个CP对应2个DP,实际上是使用了200*200个DP来显示的,也就是经过了拉伸,因而显得并不清晰。

解决方案呢?网络上流传比较广的一篇文章是iPhone4 高清晰web应用开发,出自支付宝的前端工程师

他提到的方法主要基于css query,即

<link type="text/css" rel="stylesheet"href="default.css"><!--for original iPhone-->
<link rel='stylesheet'href='default.css' media='all and (-webkit-device-pixel-ratio: 2)' /><!--for iPhone four-->
</link>

在link标签中使用media条件和-webkit-device-pixel-ratio:2属性识别出高分辨率的iPhone4,然后为其引入定制的css文件,在这个css文件中,使用另一套单独定制的图片.

比如你的站点logo的标准尺寸为80*40,针对iPhone4的话,单独使用160*80的图,但仍然指定为80*40的宽高,这样实际上使得图片中的每一个CP都对应一个DP,看起来就清晰了.

这个方法看起来不错,但缺点也很明显:你需要维护两套样式表和两套图片,而且,对于非css控制的未知宽高的图片来说,仍然不清晰。

我在实践中摸索出另外两种方法:

1、页面本身就按640px的宽度设计,然后将viewport的宽度直接设为640px.可以参看http://3g.163.com/m/iphone/ .效果堪称完美——除了一点,4代以前的机型,只能显示页面的一半。话说,除了可能流量上多一些以外,还有什么明显的缺陷吗?

2、为什么要使用两套样式表和图片呢?我全部都用高清图,然后在css中宽高截半不就可以了吗?对于img标签来说,当然是没问题。但是,如果是作为背景图的图片呢?我们可以使用css3中新加的background-size属性。还是上面那个站点logo的例子,使用background-size:50%或者background-size:80px 40px即可. 可以参看http://3g.163.com/touch/ 可惜仍然没法解决未知宽高的img标签图片不清晰的问题.

[译]避免常见的六种HTML5错误用法

原文链接请点击此处,作者为.

本文为原创翻译,同时对原文做了一些简化处理。本文遵循署名-非商业性使用协议

一、不要使用section作为div的替代品

人们在标签使用中最常见到的错误之一就是随意将HTML5的section等价于div——具体地说,就是直接用作替代品(用于样式)。在XHTML或者HTML4中,我们常看到这样的代码:

<!-- HTML 4-style code -->
<div id="wrapper">
  <div id="header">
    <h1>My super duper page</h1>
    <!-- Header content -->
  </div>
  <div id="main">
    <!-- Page content -->
  </div>
  <div id="secondary">
    <!-- Secondary content -->
  </div>
  <div id="footer">
    <!-- Footer content -->
  </div>
</div>

而现在在HTML5中,会是这样:

<!-- 请不要复制这些代码!这是错误的! -->
<section id="wrapper">
  <header>
    <h1>My super duper page</h1>
    <!-- Header content -->
  </header>
  </section><section id="main">
    <!-- Page content -->
  </section>
  <section id="secondary">
    <!-- Secondary content -->
  </section>
  <footer>
    <!-- Footer content -->
  </footer>

这样使用并不正确:section并不是样式容器section元素表示的是内容中用来帮助构建文档概要的语义部分。它应该包含一个头部。如果你想找一个用作页面容器的元素(就像HTML或者XHTML的风格),那么考虑如Kroc Camen所说,直接把样式写到body元素上吧。如果你仍然需要额外的样式容器,还是继续使用div吧。

基于上述思想,下面才是正确的使用HTML5和一些ARIA roles特性的例子(注意,根据你自己的设计,你也可能需要加入div)

<body>
  <header>
    <h1>My super duper page</h1>
    <!-- Header content -->
  </header>
  <div role="main">
    <!-- Page content -->
  </div>
  <aside role="complementary">
    <!-- Secondary content -->
  </aside>
  <footer>
    <!-- Footer content -->
  </footer>
</body>

如果你还是无法确定使用哪种元素,那么我建议你参考HTML5 sectioning content element flowchart.

二、只在需要的时候使用header和hgroup

写不需要写的标签当然是毫无意义的。不幸的是,我经常看到header和hgroup被无意义的滥用。你可以阅读一下关于headerhgroup元素的两篇文章做一个详细的了解,其中内容我简单总结如下:

  1. header元素表示的是一组介绍性或者导航性质的辅助文字,经常用作section的头部
  2. 当头部有多层结构时,比如有子头部,副标题,各种标识文字等,使用hgroup将h1-h6元素组合起来作为section的头部

header的滥用

由于header可以在一个文档中使用多次,可能使得这样的代码风格受到欢迎:

<!-- 请不要复制这段代码!此处并不需要header -->
<article>
  <header>
    <h1>My best blog post</h1>
  </header>
  <!-- Article content -->
</article>

如果你的header元素只包含一个头部元素,那么丢弃header元素吧。既然article元素已经保证了头部会出现在文档概要中,而header又不能包含多个元素(如上文所定义的),那么为什么要写多余的代码。简单点写成这样就行了:

<article>
  <h1>My best blog post</h1>
  <!-- Article content -->
</article>

hgroup的错误使用

在headers这个主题上,我也经常看到hgroup的错误使用。有时候不应该同时使用hgroup和header:

  1. 如果只有一个子头部
  2. 如果hgroup自己就能工作的很好。。。这不废话么

第一个问题一般是这样的:

<!-- 请不要复制这段代码!此处不需要hgroup -->
<header>
  <hgroup>
    <h1>My best blog post</h1>
  </hgroup>
  <p>by Rich Clark</p>
</header>

此例中,直接拿掉hgroup,让heading果奔吧。

<header>
  <h1>My best blog post</h1>
  <p>by Rich Clark</p>
</header>

第二个问题是另一个不必要的例子:

<!-- 请不要复制这段代码!此处不需要header -->
<header>
  <hgroup>
    <h1>My company</h1>
    <h2>Established 1893</h2>
  </hgroup>
</header>

如果header唯一的子元素是hgroup,那还要header干神马?如果header中没有其他的元素(比如多个hgroup),还是直接拿掉header吧

<hgroup>
  <h1>My company</h1>
  <h2>Established 1893</h2>
</hgroup>

关于

更多的例子和解释,请参阅相关文章.

三、不要把所有列表式的链接放在nav里

随着HTML5引入了30个新元素(截止到原文发布时),我们在构造语义化和结构化的标签时的选择也变得有些不慎重。也就是说,我们不应该滥用超语义化的元素。不幸的是,nav就是这样一个被滥用的例子。nav元素的规范描述如下:

nav元素表示页面中链接到其他页面或者本页面其他部分的区块;包含导航连接的区块。

注意:不是所有页面上的链接都需要放在nav元素中——这个元素本意是用作主要的导航区块。举个具体的例子,在footer中经常会有众多的链接,比如服 务条款,主页,版权声明页等等。footer元素自身已经足以应付这些情况,虽然nav元素也可以用在这里,但通常我们认为是不必要的。

WHATWG HTML spec

关键的词语是“主要的”导航。当然我们可以互相喷上一整天什么叫做“主要的”。而我个人是这样定义的:

  1. 主要的导航
  2. 站内搜索
  3. 二级导航(略有争议)
  4. 页面内导航(比如很长的文章)

既然并没有绝对的对错,所以根据一个非正式投票以及我自己的解释,以下的情况,不管你放不放,我反正放在nav中:

  1. 分页控制
  2. 社交链接(虽然有些社交链接也是主要导航,比如“关于”“收藏”)
  3. 博客文章的标签
  4. 博客文章的分类
  5. 三级导航
  6. 过长的footer

如果你不确定是否要将一系列的链接放在nav中,问你自己:“它是主要的导航吗?”为了帮助你回答这个问题,考虑以下首要原则:

  1. 如果使用section和hx也同样合适,那么不要用nav — Hixie on IRC
  2. 为了方便访问,你会在某个“快捷跳转”中给这个nav标签加一个链接吗?

如果这些问题的答案是“不”,那就跟nav鞠个躬,然后独自离开吧。

四、figure元素的常见错误

figure以及figcaption的正确使用,确实是难以驾驭。让我们来看看一些常见的错误,

不是所有的图片都是figure

上文中,我曾告诉各位不要写不必要的代码。这个错误也是同样的道理。我看到很多网站把所有的图片都写作figure。看在图片的份上请不要给它加额外的标签了。你只是让你自己蛋疼,而并不能使你的页面内容更清晰。

规范中将figure描述为“一些流动的内容,有时候会有包含于自身的标题说明。一般在文档流中会作为独立的单元引用。”这正是figure的美妙之处——它可以从主内容页移动到sidebar中,而不影响文档流。

这些问题也包含在之前提到的HTML5 element flowchart中。
如果纯粹只是为了呈现的图,也不在文档其他地方引用,那就绝对不是figure。其他视情况而定,但一开始可以问自己:“这个图片是否必须和上下文有关?”如果不是,那可能也不是figure(也许是个aside)。继续:“我可以把它移动到附录中吗?”如果两个问题都符合,则它可能是figure.

Logo并不是figure

进一步的说,logo也不适用于figure。下面是我常见的一些代码片段:

<!-- 请不要复制这段代码!这是错的 -->
<header>
  <h1>
    <figure>
      <img src="/img/mylogo.png" alt="My company" class="hide" />
    </figure>
    My company name
  </h1>
</header>
<!-- 请不要复制这段代码!这也是错的 -->
<header>
  <figure>
    <img src="/img/mylogo.png" alt="My company" />
  </figure>
</header>

没什么好说的了。这就是很普通的错误。logo是否应该是H1标签并不是我们这里讨论的焦点。真正的问题在于figure元素的滥用。figure只应该被引用在文档中,或者被section元素围绕。我想你的logo并不太可能以这样的方式引用吧。很简单,请勿使用figure。你只需要这样做:

<header>
  <h1>My company name</h1>
  <!-- More stuff in here -->
</header>

Figure也不仅仅只是图片

另一个常见的关于figure的误解是它只被图片使用。figure可以是视频,音频,图表,一段引用文字,表格,一段代码,一段散文,以及任何它们或者其他的组合。不要把figure局限于图片。web标准的职责是精确的用标签描述内容。

五、不要使用不必要的type属性

这是个常见的问题,但并不是一个错误,我认为我们应该通过最佳实践来避免这种风格。

在HTML5中,script和style元素不再需要type属性。然而这些很可能会被你的CMS自动加上,所以要移除也不是那么的轻松。但如果你是手工编码或者你完全可以控制你的模板的话,那真的没有什么理由再去包含type属性。所有的浏览器都认为脚本是javascript而样式是css样式,你没必要再多此一举了。

<!-- 请不要复制这段代码!它太冗余了! -->
<link type="text/css" rel="stylesheet"href="css/styles.css?px!=luoqingming.com" />
<script type="text/javascript"src="js/scripts?px!=luoqingming.com" />

其实只需要这样写:

<link rel="stylesheet"href="css/styles.css?px!=luoqingming.com" />
<script src="js/scripts?px!=luoqingming.com" />

甚至指定字符集的代码都可以做一些简化处理。Mark Pilgrim在Dive into HTML5的语义化一章中作出了解释。

六、form属性的错误使用

HTML5引入了一些form的新属性,以下是一些使用上的注意事项:

布尔属性

一些多媒体元素和其他元素也具有布尔属性。这里所说的规则也同样适用。

有一些新的form属性是布尔型的,意味着它们只要出现在标签中,就保证了相应的行为已经设置。这些属性包括:

  • autofocus
  • autocomplete
  • required

坦白的说,我很少看到这样的。以required为例,常见的是下面这种:

<!-- 请不要复制这段代码! 这是错的! -->
<input type="email" name="email" required="true" />
<!-- 另一个错误的例子 -->
<input type="email" name="email" required="1" />

严格来说,这并没有大碍。浏览器的HTML解析器只要看到required属性出现在标签中,那么它的功能就会被应用。但是如果你反过来写equired=”false”呢?

<!-- 请不要复制这段代码! 这是错的! -->
<input type="email" name="email" required="false" />

解析器仍然会将required属性视为有效并执行相应的行为,尽管你试着告诉它不要去执行了。这显然不是你想要的。

有三种有效的方式去使用布尔属性。(后两种只在xthml中有效)

  • required
  • required=""
  • required="required"

上述例子的正确写法应该是:

<input type="email" name="email" required />

Thanks to Ian Devlin, Derek Johnson, Tady Walsh, the HTML5 Gallery curators, and the HTML5 Doctors for their input to this article.

为ios开发创建自签名证书(Self-signed Certificate)

本文主要整理引用自:http://www.ifans.com/forums/showthread.php?t=292945

测试环境如下:

Mac OS X 10.6.7

Xcode 3.2.3 with iPhone SDK 4.0

首先需要创建自签名的证书,以使xcode能编译签名过的app

  • Open up Keychain Access in Applications>Utilities.
  • Go to Keychain Access > Certificate Assistant > Create a Certificate.
  • For ‘Name’, type: [whatever you want], i used “Jailbroken Developer”.
  • For ‘Identity Type’, leave it as ‘Self Signed Root’.
  • For ‘Certificate Type’, choose ‘Code Signing’.
  • Check the box for ‘Let me override defaults’.
  • Click on Continue, and in the popup box that comes up.
  • Type in any number under ‘Serial Number’.
  • Leave ‘Validity Period (days)’ alone.
  • Click on Continue.
  • Fill in the details with whatever you want (not really necessary).
  • Click on Continue.
  • Click on Continue for the rest of the dialog boxes.

需要注意的几点是,所谓的Certificate Assistant并不在Keychain Access的界面上,而是在顶部的菜单栏里

不要改变store的位置。(我手贱把默认的login改成了system,结果后面编译就找不到证书)

创建证书成功后,证书会显示为红色untrusted,双击,展开trust,修改为always trust。

 

下一步对xcode的配置文件进行hack

1. Open /Developer/Platforms/iPhoneOS.platform/Info.plist with Property List Editor.
2. Look for all ‘XCiPhoneOSCodeSignContext’. Replace that with ‘XCCodeSignContext’. There should be two or three to replace.
3. Save the file.
4. Restart Xcode, and build your app for a device.

最后是启动xcode,打开你的项目,project - edit project settings

将Code Signing Identity和下面的Any iPhone OS Device都修改为你创建的证书的名字。

done.build your app.