HTML5 学习总结(四)——canvas绘图、WebGL、SVG
一、Canvas
canvas是HTML5中新增一个HTML5标签与操作canvas的javascript API,它可以实现在网页中完成动态的2D与3D图像技术。<canvas> 标记和 SVG以及 VML 之间的一个重要的不同是,<canvas> 有一个基于 JavaScript 的绘图 API,而 SVG 和 VML 使用一个 XML 文档来描述绘图。SVG 绘图很容易编辑与生成,但功能明显要弱一些。
canvas可以完成动画、游戏、图表、图像处理等原来需要Flash完成的一些功能。、
浏览器支持情况如下:
1.1、创建canvas元素
<canvas id="can" width="800" height="600">不支持Canvas</canvas>
以上代码创建了一个宽度为800像素,高度为600像素的canvas。不建议使用CSS样式指定宽度和高度。
canvas标签中间的内容为替代显示内容,当浏览器不支持canvas标签时会显示出来。
创建了canvas元素后,要在canvas元素上面绘制图象,首先必须获取canvas环境上下文:
canvas.getContext(画布上绘制的类型)
2d: 表示2维
experimental-webgl: 表示试验版3维
webgl:表示3维
Hello Wolrd示例代码:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>canvas绘图1</title>
</head>
<body>
<canvas id="canvas1" width="800" height="600"></canvas>
<script type="text/javascript">
//获得画布元素
var canvas1=document.getElementById("canvas1");
//获得2维绘图的上下文
var ctx=canvas1.getContext("2d");
//设置线宽
ctx.lineWidth=10;
//设置线的颜色
ctx.strokeStyle="blue";
//将画笔移动到00点
ctx.moveTo(0,0);
//画线到800,600的坐标
ctx.lineTo(800,600);
//执行画线
ctx.stroke();
</script>
</body>
</html>
运行效果:
在页面上就显示了一条直线,另存为后就是一张背景透明的png图片。
练习:画一个100X100的正方形在画布正中央
1.2、画线
context.moveTo(x,y)
把画笔移动到x,y坐标,建立新的子路径。
context.lineTo(x,y)
建立上一个点到x,y坐标的直线,如果没有上一个点,则等同于moveTo(x,y),把(x,y)添加到子路径中。
context.stroke()
描绘子路径
//设置线宽
ctx.lineWidth = 10;
//设置线的颜色
ctx.strokeStyle = "blue";
//将画笔移到x0,y0处
context.moveTo(x0, y0);
//从x0,y0到x1,y1画一条线
ontext.lineTo(x1, y1);
//从x1,y1到x2,y2画条线
ontext.lineTo(x2, y2);
//执行填充
ontext.fill();
//执行画线
context.stroke();
结合javascript事件实现鼠标自由划线:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>canvas绘图2</title>
</head>
<body>
<canvas id="canvas1" width="800" height="600"></canvas>
<script type="text/javascript">
//获得画布元素
var canvas1 = document.getElementById("canvas1");
//获得2维绘图的上下文
var ctx = canvas1.getContext("2d");
//设置线宽
ctx.lineWidth = 10;
//设置线的颜色
ctx.strokeStyle = "blue";
canvas1.onmousemove=function(e){
//划线到当前客户端的x与y座标
ctx.lineTo(e.clientX, e.clientY);
//执行画线
ctx.stroke();
}
</script>
</body>
</html>
运行效果:
移动手机端:
1.2.1、路径与closePath,beginPath,fill
canvas的环境上下文中总有唯一一个路径,路径包含多个子路径,这些子路径可以看成是一系列点的集合。
beginPath()
清空子路径,一般用于开始路径的创建。在几次循环地创建路径的过程中,每次开始创建时都要调用beginPath函数。
closePath()
如果当前子路径是打开的,就关闭它。否则把子路径中的最后一个点和路径中的第一个点连接起来,形成闭合回路。
canvas绘图有两种模式,一种是fill,一种是stroke,fill是填充,stroke是描边线,fillstyle,strokeStyle指定绘图样式
示例代码:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>路径与closePath,beginPath,fill</title>
</head>
<body>
<canvas id="canvas1" width="600" height="600"></canvas>
<script type="text/javascript">
//获得画布元素
var canvas1 = document.getElementById("canvas1");
//获得2维绘图的上下文
var ctx = canvas1.getContext("2d");
//设置线宽
ctx.lineWidth = 10;
//设置线的颜色
ctx.strokeStyle = "blue";
ctx.moveTo(0,0); //移动画笔到0,0点
ctx.lineTo(300,300); //画线到300,300的位置
ctx.stroke(); //执行描边
ctx.beginPath(); //清空子路径,一般用于开始路径的创建
ctx.strokeStyle = "red";
ctx.moveTo(300,300);
ctx.lineTo(0,595); //画线到0,300的位置
ctx.lineTo(595,595); //画线到右下角
ctx.closePath(); //闭合
//ctx.stroke(); //执行描边
ctx.fillStyle="lightgreen"; //设置填充颜色
ctx.fill(); //执行填充
</script>
</body>
</html>
运行效果:
练习:试着完成一个象棋或围棋棋盘。
1.3、绘制矩形
context.strokeRect(x,y,width,height)
以x,y为左上角,绘制宽度为width,高度为height的矩形。
context.fillRect(x,y,width,height)
以x,y为左上角,填充宽度为width,高度为height的矩形。
context.clearRect(x,y,width,height)
清除以x,y为左上角,宽度为width,高度为height的矩形区域。
示例代码:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>绘制矩形</title>
</head>
<body>
<canvas id="canvas1" width="600" height="600"></canvas>
<script type="text/javascript">
//获得画布元素
var canvas1 = document.getElementById("canvas1");
//获得2维绘图的上下文
var ctx = canvas1.getContext("2d");
//设置线宽
ctx.lineWidth = 10;
//设置线的颜色
ctx.strokeStyle ="dodgerblue";
//画一个空心的矩形,
ctx.strokeRect(0,0,600,600);
//画一个实心矩形
ctx.fillStyle="aquamarine";
ctx.fillRect(200,200,200,200);
//清除指定的矩形区域
ctx.clearRect(250,250,100,100);
</script>
</body>
</html>
运行效果:
1.4、绘制圆弧
context.arc(x,y,radius,startAngle,endAngle,anticlockwise)
arc方法用来绘制一段圆弧路径,以(x,y)圆心位置radius为半径、startAngle为起始弧度、endAngle为终止弧度来,而在画圆弧时的旋转方向则由最后一个参数 anticlockwise 来指定,如果为 true 就是逆时针,false 则为顺时针,Math.PI * 2 刚好为一周。
示例代码:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>绘制圆弧</title>
</head>
<body>
<canvas id="canvas1" width="600" height="600"></canvas>
<script type="text/javascript">
//获得画布元素
var canvas1 = document.getElementById("canvas1");
//获得2维绘图的上下文
var ctx = canvas1.getContext("2d");
//设置线宽
ctx.lineWidth = 10;
//设置线的颜色
ctx.strokeStyle ="dodgerblue";
//画一段圆弧,300,300是圆心,200是半径,0是超始角度,Math.PI是结束角度,是否逆时钟
ctx.arc(300,300,200,0,Math.PI,false);
//闭合
ctx.closePath();
ctx.stroke();
ctx.beginPath();
ctx.fillStyle="aquamarine";
ctx.arc(300,300,100,0,Math.PI*2,false);
ctx.fill();
</script>
</body>
</html>
运行效果:
练习:
a、模拟钟表的时,分,秒
b、模拟水波,一个黑色的屏幕,多个从中心随机产生彩色的圈不断的放大,接触到屏幕结束。
1.5、绘制图像
context.drawImage(image,x,y)
把image图像绘制到画布上x,y坐标位置。
context.drawImage(image,x,y,w,h)
把image图像绘制到画布上x,y坐标位置,图像的宽度是w,高度是h。
context.drawImage(image,sx,sy,sw,sh,dx,dy,dw,dh)
截取image图像以sx,sy为左上角坐标,宽度为sw,高度为sh的一块矩形区域绘制到画布上以dx,dy坐标位置,图像宽度是dw,高度是dh。
其中image可以是htmlImageElement元素,htmlcanvasElement元素,htmlVideoElement元素
示例代码:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>绘制图像</title>
</head>
<body>
<canvas id="canvas1" width="600" height="600"></canvas>
<img src="img/apple.png" id="apple" hidden="hidden" />
<script type="text/javascript">
//必须当页面中的图片资源加载成功
window.onload = function() {
//获得画布元素
var canvas1 = document.getElementById("canvas1");
//获得2维绘图的上下文
var ctx = canvas1.getContext("2d");
//设置线宽
ctx.lineWidth = 10;
//设置线的颜色
ctx.strokeStyle = "dodgerblue";
ctx.moveTo(0,0);
ctx.strokeRect(0,0,600,600);
//图片
var apple = document.getElementById("apple");
//将图像绘制到画布的,图片的左上角
ctx.drawImage(apple, 300-52, 300-63);
}
</script>
</body>
</html>
运行效果:
1.6、绘制文字
context.fillText(text,x,y,[maxWidth])
在canvas上填充文字,text表示需要绘制的文字,x,y分别表示绘制在canvas上的横,纵坐标,最后一个参数可选,表示显示文字的最大宽度,防止文字显示溢出。
context.strokeText(text,x,y,[maxWidth])
在canvas上描边文字,参数的意义同fillText
使用context.font属性设置字体
context.font='italic bolder 48px 黑体';
示例代码:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>绘制文字</title>
</head>
<body>
<canvas id="canvas1" width="600" height="600"></canvas>
<img src="img/apple.png" id="apple" hidden="hidden" />
<script type="text/javascript">
//必须当页面中的图片资源加载成功
window.onload = function() {
//获得画布元素
var canvas1 = document.getElementById("canvas1");
//获得2维绘图的上下文
var ctx = canvas1.getContext("2d");
//设置线宽
ctx.lineWidth = 1;
//设置线的颜色
ctx.strokeStyle = "dodgerblue";
ctx.moveTo(0,0);
ctx.strokeRect(0,0,600,600);
//绘制文字
//描边
ctx.font="50px microsoft yahei";
ctx.strokeText("Hello Zhangguo",20,100);
//填充
ctx.fillStyle=
ctx.fillText("Hello Zhangguo",20,200);
}
</script>
</body>
</html>
运行结果:
1.7、随机颜色与简单动画
主要结合随机方法与定时器、时钟实现简单的动画。
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>随机颜色与简单动画</title>
</head>
<body>
<canvas id="canvas1" width="1000" height="650"></canvas>
<img src="img/apple.png" id="apple" hidden="hidden" />
<script type="text/javascript">
var magicCircle = {
randomColor: function() {
return "#" + parseInt(Math.random() * 16777216).toString(16);
},
getNum: function(min, max) {
return parseInt(Math.random() * (max - min)) + min;
},
r: 10,
run: function() {
//获得画布元素
this.canvas1 = document.getElementById("canvas1");
//获得2维绘图的上下文
this.ctx = this.canvas1.getContext("2d");
//运行
setInterval(this.draw, 100);
this.bindEvent();
},
draw: function() {
magicCircle.ctx.beginPath();
magicCircle.ctx.lineWidth = magicCircle.getNum(1,10);
magicCircle.ctx.strokeStyle = magicCircle.randomColor();
magicCircle.ctx.arc(magicCircle.getNum(1,1000), magicCircle.getNum(1,600), magicCircle.r, 0, Math.PI * 2);
magicCircle.ctx.stroke();
magicCircle.r += 10;
if(magicCircle.r > 300) magicCircle.r = 10;
},
bindEvent:function()
{
this.canvas1.onmousemove=function(e){
magicCircle.ctx.lineWidth = magicCircle.getNum(1,10);
magicCircle.ctx.strokeStyle = magicCircle.randomColor();
magicCircle.ctx.arc(e.clientX, e.clientY, magicCircle.r, 0, Math.PI * 2);
magicCircle.ctx.stroke();
magicCircle.r += 10;
if(magicCircle.r > 300) magicCircle.r = 10;
}
}
};
magicCircle.run();
</script>
</body>
</html>
运行效果:
二、WebGL
WebGL(全写Web Graphics Library)是一种3D绘图标准,这种绘图技术标准允许把JavaScript和OpenGL ES 2.0结合在一起,通过增加OpenGL ES 2.0的一个JavaScript绑定,WebGL可以为HTML5 Canvas提供硬件3D加速渲染,这样Web开发人员就可以借助系统显卡来在浏览器里更流畅地展示3D场景和模型了,还能创建复杂的导航和数据视觉化。显然,WebGL技术标准免去了开发网页专用渲染插件的麻烦,可被用于创建具有复杂3D结构的网站页面,甚至可以用来设计3D网页游戏等等。
WebGL完美地解决了现有的Web交互式三维动画的两个问题:
第一,它通过HTML脚本本身实现Web交互式三维动画的制作,无需任何浏览器插件支持;
第二,它利用底层的图形硬件加速功能进行的图形渲染,是通过统一的、标准的、跨平台的OpenGL接口实现的。
通俗说WebGL中canvas绘图中的3D版本。因为原生的WebGL很复杂,我们经常会使用一些三方的库,如three.js等,这些库多数用于HTML5游戏开发。
Three.js的示例代码:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Three.js</title>
</head>
<body>
<script src="js/three.min.js" type="text/javascript" charset="utf-8"></script>
<script type="text/javascript">
var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
var renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
var geometry = new THREE.CubeGeometry(1, 1, 1);
var material = new THREE.MeshBasicMaterial({
color: 0x0000ff
});
var cube = new THREE.Mesh(geometry, material);
scene.add(cube);
camera.position.z = 5;
function render() {
requestAnimationFrame(render);
cube.rotation.x += 0.1;
cube.rotation.y += 0.1;
renderer.render(scene, camera);
}
render();
</script>
</body>
</html>
three.js示例运行结果:
2.1、HTML5游戏开发
随着HTML5的发展与硬件性能的提升HTML5游戏开发越来越受到游戏开发者的重视,因为WebGL存在一定的复杂度,所有产生了许多优秀的开源HTML5游戏引擎,下面是github上开源免费的HTML5游戏引擎:
Name | Updated Time | Watch | Star | Fork | Commits | Contributors |
---|---|---|---|---|---|---|
Three.js | 2016/3/28 | 1590 | 24041 | 7768 | 14825 | 588 |
Phaser | 2016/2/18 | 837 | 11782 | 4095 | 4423 | 206 |
Pixi.js | 2016/3/17 | 656 | 10063 | 1942 | 2860 | 161 |
egret | 2016/3/30 | 215 | 1275 | 303 | 4268 | 25 |
enchantjs | 2016/1/4 | 185 | 1445 | 301 | 1683 | 27 |
crafty | 2016/3/21 | 134 | 2050 | 473 | 1807 | 106 |
turbulenz | 2015/11/23 | 271 | 2544 | 406 | 1737 | 13 |
cocos2d-js | 2016/3/30 | 162 | 1207 | 469 | 4559 | 45 |
playcanvas | 2016/3/30 | 164 | 1784 | 368 | 5142 | 16 |
melonjs | 2016/3/30 | 13 | 1579 | 371 | 3907 | 40 |
quintus | 2016/2/3 | 136 | 1023 | 412 | 256 | 33 |
Hilo | 2016/2/3 | 173 | 2449 | 340 | 20 | 2 |
2.2.1、Cocos2D-HTML5
开源,免费的HTML5 2D游戏开发框架,Cocos2D拥有几个主要版本,包括Cocos2D-iPhone、Cocos2D-X,以及被社区普遍看好的Cocos2D-HTML5和JavaScriptbindings for Cocos2D-X。CocoStudio工具集是开源游戏引擎。特点:与Cocos2d的API类似,容易上手、中文文档齐全,资料丰富、基于MIT协议的开源引擎。它由国内Cocos2d-x核心团队主导开发和维护,行业领袖、HTML5大力推动者Google为这个项目提供支持。
github:https://github.com/cocos2d/cocos2d-html5
HelloWorld示例:
<!DOCTYPE html>
<html>
<head>
<title>Hello Cocos2d-JS</title>
</head>
<body>
<canvas id="gameCanvas" width="800" height="450"></canvas>
<script type="text/javascript" src="cocos2d-js-v3.12-lite.js" charset="UTF-8"></script>
<script type="text/javascript">
window.onload = function(){
cc.game.onStart = function(){
//load resources
cc.LoaderScene.preload(["HelloWorld.png"], function () {
var MyScene = cc.Scene.extend({
onEnter:function () {
this._super();
var size = cc.director.getWinSize();
var sprite = cc.Sprite.create("HelloWorld.png");
sprite.setPosition(size.width / 2, size.height / 2);
sprite.setScale(0.8);
this.addChild(sprite, 0);
var label = cc.LabelTTF.create("Hello World", "Arial", 40);
label.setPosition(size.width / 2, size.height / 2);
this.addChild(label, 1);
}
});
cc.director.runScene(new MyScene());
}, this);
};
cc.game.run("gameCanvas");
};
</script>
</body>
</html>
运行结果:
2.2.2、Egret(白鹭引擎)
是一个基于TypeScript语言开发的HTML5游戏引擎,围住神经猫就是用这个框架开发的。
特点:
a)、基于TypeScript及JavaScript技术,支持Flash到Egret高效转换,引擎、工具、运行时完整工作流
b)、跨平台:HTML5,iOS,Android,Windows Phone
c)、全中文文档:文档与开发者社区齐全
d)、开源免费,BSD开源协议、任意定制及扩展
三、SVG
SVG可缩放矢量图形(Scalable Vector Graphics)是基于可扩展标记语言(XML),用于描述二维矢量图形的一种图形格式。SVG是W3C("World Wide Web ConSortium" 即 " 国际互联网标准组织")在2000年8月制定的一种新的二维矢量图形格式,也是规范中的网络矢量图形标准。SVG严格遵从XML语法,并用文本格式的描述性语言来描述图像内容,因此是一种和图像分辨率无关的矢量图形格式。SVG 于 2003 年 1 月 14 日成为 W3C 推荐标准。
特点:
1.任意放缩
用户可以任意缩放图像显示,而不会破坏图像的清晰度、细节等。
2.文本独立
SVG图像中的文字独立于图像,文字保留可编辑和可搜寻的状态。也不会再有字体的限制,用户系统即使没有安装某一字体,也会看到和他们制作时完全相同的画面。
3.较小文件
总体来讲,SVG文件比那些GIF和JPEG格式的文件要小很多,因而下载也很快。
4.超强显示效果
SVG图像在屏幕上总是边缘清晰,它的清晰度适合任何屏幕分辨率和打印分辨率。
5.超级颜色控制
SVG图像提供一个1600万种颜色的调色板,支持ICC颜色描述文件标准、RGB、线X填充、渐变和蒙版。
6.交互X和智能化。SVG面临的主要问题一个是如何和已经占有重要市场份额的矢量图形格式Flash竞争的问题,另一个问题就是SVG的本地运行环境下的厂家支持程度。
浏览器支持:
Internet Explorer9,火狐,谷歌Chrome,Opera和Safari都支持SVG。
IE8和早期版本都需要一个插件 - 如Adobe SVG浏览器,这是免费提供的。
3.1、SVG Hello Wrold
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>SVG Hello World</title>
</head>
<body>
<svg xmlns="http://www.w3.org/2000/svg" version="1.1">
<circle cx="100" cy="100" r="30" stroke="blue" stroke-width="2" fill="red" />
</svg>
</body>
</html>
运行结果:
svg是一个新增加标签,xmlns是命名空间,version是svg的版本,circle标签就是对svg要展示的图像进行描述,cx与cy表示位置,r表示半径,stroke是描边样式,stroke-width就线宽,fill是填充样式。浏览器兼容性很好:
3.2、多种引入SVG的方法
SVG 文件可通过以下标签嵌入 HTML 文档:<embed>、<object> 或者 <iframe>。
SVG的代码可以直接嵌入到HTML页面中,或您可以直接链接到SVG文件
引入方式如下:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>引入SVG的方法</title>
<style type="text/css">
body{
background:url(me.svg);
}
</style>
</head>
<body>
<h2>embed</h2>
<embed src="me.svg" type="image/svg+xml" width="108" height="108" /> 优势:所有主要浏览器都支持,并允许使用脚本 缺点:不推荐在HTML4和XHTML中使用(但在HTML5允许)
<h2>object</h2>
<object data="me.svg" type="image/svg+xml" width="108" height="108"></object> 优势:所有主要浏览器都支持,并支持HTML4,XHTML和HTML5标准 缺点:不允许使用脚本。
<h2>iframe</h2>
<iframe src="me.svg" frameborder="0" width="108" height="108"></iframe> 优势:所有主要浏览器都支持,并允许使用脚本 缺点:不推荐在HTML4和XHTML中使用(但在HTML5允许)
<h2>直接嵌入</h2>
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="108" height="108">
<circle cx="54" cy="54" r="50" stroke="blue" stroke-width="2" fill="blue" />
</svg>
在Firefox、Internet Explorer9、谷歌Chrome和Safari中,你可以直接在HTML嵌入SVG代码。 注意:SVG不能直接嵌入到Opera。
<h2>image</h2>
<img src="me.svg" width="108" height="108" />
</body>
</html>
运行结果:
3.3、画直线
示例代码:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Line</title>
</head>
<body>
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="500" height="500">
<line x1="0" y1="0" x2="500" y2="500" style="stroke:rgb(0,0,255);stroke-3" />
</svg>
</body>
</html>
参数:
x1 属性在 x 轴定义线条的开始
y1 属性在 y 轴定义线条的开始
x2 属性在 x 轴定义线条的结束
y2 属性在 y 轴定义线条的结束
运行结果:
3.4、画椭圆
示例代码:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>椭圆</title>
</head>
<body>
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="500" hidden="500">
<ellipse cx="300" cy="80" rx="100" ry="50" style="fill:yellow;stroke:dodgerblue;stroke-5" />
</svg>
</body>
</html>
参数:
CX属性定义的椭圆中心的x坐标
CY属性定义的椭圆中心的y坐标
RX属性定义的水平半径
RY属性定义的垂直半径
运行结果:
3.5、文本与矩形
示例代码:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>文本与矩形</title>
</head>
<body>
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="800" height="500">
<text x="0" y="50" fill="blue" style="font-size:30px; font-family: 'microsoft yahei';">My Teacher Zhangguo</text>
<rect x="40" y="60" width="260" height="260" style="fill:blue;stroke:pink;stroke-5;
fill-opacity:0.1;stroke-opacity:0.9" />
</svg>
</body>
</html>
运行结果:
3.6、向下兼容与图标
IE8并不直接兼容SVG,如果需要显示则可以使用插件,如果不使用插件也有向下兼容的办法。
示例代码:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>向下兼容与图标</title>
</head>
<body>
<svg width="78" height="78">
<image xlink:href="money.svg" width="78" height="78" src="money.png"></image>
</svg>
</body>
</html>
运行结果:
参数:
image本身就是svg中引入外部图像的元素,刚好在ie8下又能被解析。
四、示例下载
示例代码:https://github.com/zhangguo5/HTML5_4_1.git
转载:https://i.cnblogs.com/EditPosts.aspx?opt=1