push进程在小米红米机型下, 应用退出后没有保留
小米, 红米 note2 收不到离线消息
而htc可以收到离线消息
经过测试分析, 怀疑 收不到离线消息的机器在应用退出后 push 和 notify 服务没有保留
还请官方测试一下
小米, 红米 note2 收不到离线消息
而htc可以收到离线消息
经过测试分析, 怀疑 收不到离线消息的机器在应用退出后 push 和 notify 服务没有保留
还请官方测试一下
关于微信分享的实践与问题汇总,比较适合小白~
需求是要向微信朋友圈或者微信好友发送带【缩略图】【标题】【介绍性文字】【链接】的分享,也就是H5+的demo里面share.html里的链接分享
其实我自己也是第一次做这种需求,第一次不知道什么原因,分享出去的总是只有介绍性文字,而没有链接跟缩略图。后来经过请教官方群里的朋友以及自己的琢磨,成功实现了该需求,而且是运用非常小白的方式,下面做一个简单的总结:
1、把官方H5+的demo里的share.html直接copy到自己项目文件中,把对应引入文件的路径修改正确。
2、如果不想或者不会删减share.html里面跟【链接分享】无关的代码,就直接把原有的HTML代码display:none就可以了,这样,部分js不会因为找不到对应的DOM元素而报错(当然这个方法不推荐,只是给新手做参考),比如
<div id="dcontent" class="dcontent" style="display: none;">
3、接下来就是在share.html页面内部的js中找到shareAction(sb, bh)函数,把控制【缩略图】【标题】【介绍性文字】【链接】的地方赋值为我们自己的内容即可。(在下面的代码的被**包起来的那几行)
function shareAction(sb, bh) {
outSet("分享操作:");
if (!sb || !sb.s) {
outLine("无效的分享服务!");
return;
}
var msg = {
**content: sharecontent.value**,
extra: {
scene: sb.x
}
};
if (bh) {
**msg.href = sharehref.value;**
if (sharehrefTitle && sharehrefTitle.value != "") {
msg.title = sharehrefTitle.value;
}
if (sharehrefDes && sharehrefDes.value != "") {
msg.content = sharehrefDes.value;
}
// msg.thumbs = ["_www/logo.png"];
**msg.thumbs = [document.getElementById('pic').src];**
// _www/logo.png
msg.pictures = ["_www/logo.png"];
} else {
if (pic && pic.realUrl) {
msg.pictures = [pic.realUrl];
}
}
// 发送分享
if (sb.s.authenticated) {
outLine("---已授权---");
shareMessage(msg, sb.s);
} else {
outLine("---未授权---");
sb.s.authorize(function() {
shareMessage(msg, sb.s);
}, function(e) {
outLine("认证授权失败:" + e.code + " - " + e.message);
});
}
}
4、下面是我给以上函数赋值自己新的内容的代码。
mui("#spread-cont").on("tap","li",function(){
var Href=document.getElementById('sharehref');
var Title=document.getElementById('sharehrefTitle');
var Des=document.getElementById('sharehrefDes');
var shareCont=document.getElementById('sharecontent');
var sharePic=document.getElementById('pic');
Href.value=data[this.getAttribute("data-id")].Url;
Title.value=data[this.getAttribute("data-id")].RwName;
Des.value=data[this.getAttribute("data-id")].Content;
shareCont.value=data[this.getAttribute("data-id")].Content;
sharePic.src=data[this.getAttribute("data-id")].Rwtp;
shareHref();
});
5、里面的data[this.getAttribute("data-id")],是我通过ajax获取到的json数据,在该json数据里取出来我自己的【缩略图】【标题】【介绍性文字】【链接】,并进行赋值操作。
6、哈哈,上面的方法非常小白,好怕被大神看到啊~
需求是要向微信朋友圈或者微信好友发送带【缩略图】【标题】【介绍性文字】【链接】的分享,也就是H5+的demo里面share.html里的链接分享
其实我自己也是第一次做这种需求,第一次不知道什么原因,分享出去的总是只有介绍性文字,而没有链接跟缩略图。后来经过请教官方群里的朋友以及自己的琢磨,成功实现了该需求,而且是运用非常小白的方式,下面做一个简单的总结:
1、把官方H5+的demo里的share.html直接copy到自己项目文件中,把对应引入文件的路径修改正确。
2、如果不想或者不会删减share.html里面跟【链接分享】无关的代码,就直接把原有的HTML代码display:none就可以了,这样,部分js不会因为找不到对应的DOM元素而报错(当然这个方法不推荐,只是给新手做参考),比如
<div id="dcontent" class="dcontent" style="display: none;">
3、接下来就是在share.html页面内部的js中找到shareAction(sb, bh)函数,把控制【缩略图】【标题】【介绍性文字】【链接】的地方赋值为我们自己的内容即可。(在下面的代码的被**包起来的那几行)
function shareAction(sb, bh) {
outSet("分享操作:");
if (!sb || !sb.s) {
outLine("无效的分享服务!");
return;
}
var msg = {
**content: sharecontent.value**,
extra: {
scene: sb.x
}
};
if (bh) {
**msg.href = sharehref.value;**
if (sharehrefTitle && sharehrefTitle.value != "") {
msg.title = sharehrefTitle.value;
}
if (sharehrefDes && sharehrefDes.value != "") {
msg.content = sharehrefDes.value;
}
// msg.thumbs = ["_www/logo.png"];
**msg.thumbs = [document.getElementById('pic').src];**
// _www/logo.png
msg.pictures = ["_www/logo.png"];
} else {
if (pic && pic.realUrl) {
msg.pictures = [pic.realUrl];
}
}
// 发送分享
if (sb.s.authenticated) {
outLine("---已授权---");
shareMessage(msg, sb.s);
} else {
outLine("---未授权---");
sb.s.authorize(function() {
shareMessage(msg, sb.s);
}, function(e) {
outLine("认证授权失败:" + e.code + " - " + e.message);
});
}
}
4、下面是我给以上函数赋值自己新的内容的代码。
mui("#spread-cont").on("tap","li",function(){
var Href=document.getElementById('sharehref');
var Title=document.getElementById('sharehrefTitle');
var Des=document.getElementById('sharehrefDes');
var shareCont=document.getElementById('sharecontent');
var sharePic=document.getElementById('pic');
Href.value=data[this.getAttribute("data-id")].Url;
Title.value=data[this.getAttribute("data-id")].RwName;
Des.value=data[this.getAttribute("data-id")].Content;
shareCont.value=data[this.getAttribute("data-id")].Content;
sharePic.src=data[this.getAttribute("data-id")].Rwtp;
shareHref();
});
5、里面的data[this.getAttribute("data-id")],是我通过ajax获取到的json数据,在该json数据里取出来我自己的【缩略图】【标题】【介绍性文字】【链接】,并进行赋值操作。
6、哈哈,上面的方法非常小白,好怕被大神看到啊~
收起阅读 »给DCloud产品的个人建议
背景:我懂原生 懂Html,Css,Js 三周前了解到了Hbuilder
我的需求 像支付宝一样的开发(原生+ html5) 支付宝APP中有不少应用如:彩票功能,外卖,滴滴等 尤其是那个外卖 就是一个单独的webapp
我也想在自己原生APP中嵌入各个完全不同的webapp,能点击原生控件进入webapp
因为此需求 我好好了解了下Hbuilder 确实有些模糊 花了两个周我加了所有和Builder的群 去每个群里下载了所有的demo 研究后才看懂整个开发流程
因此 有些建议想说说:
1 网站顶部导航增加新手教程 如何一步步从无到有地开发 言简意赅地描述 哪种情景能做什么事情?
2 网站问答感觉可以再细分问题 找相似疑问都不好找哦 感觉论坛更适合开发者 论坛分类整理和搜集有demo的帖子
3 整个DC的产品我觉得可以分为三类 一类是:纯Html5 也就是Runtime环境开发 一类是:原生+webapp开发(也就是widget开发) 一类是:webview开发 应该明确告知新手以便引导学习方向 学习是有时间成本的
4 网站应该将新手分为几类人群 新手也是有类型可分的 比如我:我懂原生开发 有2年工作经验 但是为什么我也是新手呢 因为我才了解Hbuilder开发 不懂这个东西 就得学习 网站应该将新手分为掌握的技能类型 比如:仅懂原生的新手 、 懂原生且懂Html5 css js的新手 、仅懂Html5 css js的新手 、啥都不懂的新手 这四类 分别引导进入不同教程
5 Dc产品未覆盖我的需求这样的效果 建议增加 比如 我想实现的效果是 跟支付宝打开 “外卖” 这个功能按钮的图标后 是从原生webview打开的 打开后是一个webapp应用 DC产品未实现这样的方式 DC产品应该是打开后还有引导页 且 进度条是一个加载项 但我想给客户的感觉就像网页一样打开 也就是想实现的是 原生webview打开widget这个webapp的效果 有进度条 就算不通过webview打开 按照DC产品的widget打开也可否增加一个类似这样效果的 没有引导页的 效果 综上所述 也就是没有在原生APP中像支付宝这样过渡到webapp的效果 是否应该增加一个混合开发模式 现在大部分都是混合开发的
6 新手可能会问 怎么在plus中增加mui框架 怎么在mui框架中调用plus 官方应该给予这个问题明确答案 我想新手都可能会问这个问题 因为Hbuilder中的两个demo感觉就像独立的 怎么整合到一块
感觉DC产品挺好的 这就是我使用过程中的疑问 我相信会有不少新手跟我一样有此疑问和建议
背景:我懂原生 懂Html,Css,Js 三周前了解到了Hbuilder
我的需求 像支付宝一样的开发(原生+ html5) 支付宝APP中有不少应用如:彩票功能,外卖,滴滴等 尤其是那个外卖 就是一个单独的webapp
我也想在自己原生APP中嵌入各个完全不同的webapp,能点击原生控件进入webapp
因为此需求 我好好了解了下Hbuilder 确实有些模糊 花了两个周我加了所有和Builder的群 去每个群里下载了所有的demo 研究后才看懂整个开发流程
因此 有些建议想说说:
1 网站顶部导航增加新手教程 如何一步步从无到有地开发 言简意赅地描述 哪种情景能做什么事情?
2 网站问答感觉可以再细分问题 找相似疑问都不好找哦 感觉论坛更适合开发者 论坛分类整理和搜集有demo的帖子
3 整个DC的产品我觉得可以分为三类 一类是:纯Html5 也就是Runtime环境开发 一类是:原生+webapp开发(也就是widget开发) 一类是:webview开发 应该明确告知新手以便引导学习方向 学习是有时间成本的
4 网站应该将新手分为几类人群 新手也是有类型可分的 比如我:我懂原生开发 有2年工作经验 但是为什么我也是新手呢 因为我才了解Hbuilder开发 不懂这个东西 就得学习 网站应该将新手分为掌握的技能类型 比如:仅懂原生的新手 、 懂原生且懂Html5 css js的新手 、仅懂Html5 css js的新手 、啥都不懂的新手 这四类 分别引导进入不同教程
5 Dc产品未覆盖我的需求这样的效果 建议增加 比如 我想实现的效果是 跟支付宝打开 “外卖” 这个功能按钮的图标后 是从原生webview打开的 打开后是一个webapp应用 DC产品未实现这样的方式 DC产品应该是打开后还有引导页 且 进度条是一个加载项 但我想给客户的感觉就像网页一样打开 也就是想实现的是 原生webview打开widget这个webapp的效果 有进度条 就算不通过webview打开 按照DC产品的widget打开也可否增加一个类似这样效果的 没有引导页的 效果 综上所述 也就是没有在原生APP中像支付宝这样过渡到webapp的效果 是否应该增加一个混合开发模式 现在大部分都是混合开发的
6 新手可能会问 怎么在plus中增加mui框架 怎么在mui框架中调用plus 官方应该给予这个问题明确答案 我想新手都可能会问这个问题 因为Hbuilder中的两个demo感觉就像独立的 怎么整合到一块
感觉DC产品挺好的 这就是我使用过程中的疑问 我相信会有不少新手跟我一样有此疑问和建议
收起阅读 »判断网络是否连接
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
<title>hello world</title>
<script type="text/javascript" src="js/mui.min.js"></script>
</head>
<body>
<script type="text/javascript" charset="utf-8">
mui.plusReady(function() {
document.addEventListener("netchange", wainshow, false);
});
function wainshow() {
if (plus.networkinfo.getCurrentType() == plus.networkinfo.CONNECTION_NONE) {
mui.toast("网络异常,请检查网络设置!");
} else {
mui.toast("网络正常");
}
}
</script>
<div onclick="wainshow();">111111</div>
</body>
</html> <!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
<title>hello world</title>
<script type="text/javascript" src="js/mui.min.js"></script>
</head>
<body>
<script type="text/javascript" charset="utf-8">
mui.plusReady(function() {
document.addEventListener("netchange", wainshow, false);
});
function wainshow() {
if (plus.networkinfo.getCurrentType() == plus.networkinfo.CONNECTION_NONE) {
mui.toast("网络异常,请检查网络设置!");
} else {
mui.toast("网络正常");
}
}
</script>
<div onclick="wainshow();">111111</div>
</body>
</html> 收起阅读 »
分享个人写的用于项目的全选和取消以及全选按钮随着子类选项的状态变化而变化
<div class="mui-content">
<form class="mui-input-group">
<div class="mui-input-row mui-checkbox">
<label>全部</label>
<input id="selectall" type="checkbox">
</div>
<div class="mui-input-row mui-checkbox">
<label>四年级一班</label>
<input name="subcheck" type="checkbox">
</div>
<div class="mui-input-row mui-checkbox">
<label>四年级二班</label>
<input name="subcheck" type="checkbox">
</div>
<div class="mui-input-row mui-checkbox">
<label>四年级三班</label>
<input name="subcheck" type="checkbox">
</div>
</form>
</div>
<script type="text/javascript">
var chkall=document.getElementById("selectall");
mui.plusReady(function(){
chkall.addEventListener("change",function(){
if(this.checked){
mui("input[name='subcheck']").each(function(){
this.checked=true;
});
}else{
mui("input[name='subcheck']").each(function(){
this.checked=false;
});
}
},false);
var cbknum=mui("input[name='subcheck']").length;
var chknum=0;
mui(".mui-input-group").on("change","input[name='subcheck']",function(){
if(this.checked)
{
chknum++;
}else{
chknum--;
}
if(cbknum==chknum){
chkall.checked=true;
}else{
chkall.checked=false;
}
});
});
<div class="mui-content">
<form class="mui-input-group">
<div class="mui-input-row mui-checkbox">
<label>全部</label>
<input id="selectall" type="checkbox">
</div>
<div class="mui-input-row mui-checkbox">
<label>四年级一班</label>
<input name="subcheck" type="checkbox">
</div>
<div class="mui-input-row mui-checkbox">
<label>四年级二班</label>
<input name="subcheck" type="checkbox">
</div>
<div class="mui-input-row mui-checkbox">
<label>四年级三班</label>
<input name="subcheck" type="checkbox">
</div>
</form>
</div>
<script type="text/javascript">
var chkall=document.getElementById("selectall");
mui.plusReady(function(){
chkall.addEventListener("change",function(){
if(this.checked){
mui("input[name='subcheck']").each(function(){
this.checked=true;
});
}else{
mui("input[name='subcheck']").each(function(){
this.checked=false;
});
}
},false);
var cbknum=mui("input[name='subcheck']").length;
var chknum=0;
mui(".mui-input-group").on("change","input[name='subcheck']",function(){
if(this.checked)
{
chknum++;
}else{
chknum--;
}
if(cbknum==chknum){
chkall.checked=true;
}else{
chkall.checked=false;
}
});
});
收起阅读 »
使用纯HTML5编写一款网页上的时钟的代码分享
你需要知道的:
canvas标签只是图形容器,您必须使用脚本来绘制图形。默认大小:宽300px,高150px;
getContext() 方法可返回一个对象,该对象提供了用于在画布上绘图的方法和属性。——获取上下文对象。
getContext("2d") 对象属性和方法,可用于在画布上绘制文本、线条、矩形、圆形等等。
fillRect(l,t,w,h):默认颜色是黑色 strokeRect(l,t,w,h):带边框的方块。默认一像素黑色边框
setInterval() 方法可按照指定的周期(以毫秒计)来调用函数或计算表达式。
beginPath():定义开始绘制路径, 它把当前的点设置为 (0,0)。 当一个画布的环境第一次创建,beginPath()
方法会被显式地调用。
closePath():结束绘制路径(将起点与终点进行连接)
绘制圆形:
arc( x,y,半径,起始弧度,结束弧度,旋转方向)
x,y:起始位置
弧度与角度的关系:弧度=角度*Math.PI/180
旋转方向:顺时针(默认:false,逆时针:true)
代码:
XML/HTML Code复制内容到剪贴板
<!DOCTYPE HTML>
<html lang="en-US">
<head>
<meta charset="UTF-8">
<title></title>
<script>
window.onload = function(){
var oC = document.getElementById('ch1');
var oGC = oC.getContext('2d');
function drawClock(){
var x = 200; //指定坐标
var y = 200;
var r = 150; //指定钟表半径
oGC.clearRect(0,0,oC.width,oC.height);//清空画布
var oDate = new Date(); //创建日期对象
var oHours = oDate.getHours();//获取时间
var oMin = oDate.getMinutes();
var oSen = oDate.getSeconds();
var oHoursValue = (-90 + oHours*30 + oMin/2)*Math.PI/180; //设置时针的值
var oMinValue = (-90 + oMin*6)*Math.PI/180;
var oSenValue = (-90 + oSen*6)*Math.PI/180;
oGC.beginPath();//开始
for(var i=0;i<60;i++){ //i为60,代表着时钟的60个小刻度
oGC.moveTo(x,y);
oGC.arc(x,y,r,6*i*Math.PI/180,6*(i+1)*Math.PI/180,false); //循环从6度到12度
}
oGC.closePath();
oGC.stroke();
oGC.fillStyle ='white'; //覆盖住小刻度的黑色线
oGC.beginPath();
oGC.moveTo(x,y);
oGC.arc(x,y,r*19/20,0,360*(i+1)*Math.PI/180,false);
oGC.closePath();//结束
oGC.fill();
oGC.lineWidth = 3; //设置时钟圆盘大刻度的粗细值
oGC.beginPath(); //开始画大的时钟刻度
for(i=0;i<12;i++){ //i为12,代表着时钟刻度的12大格
oGC.moveTo(x,y);
oGC.arc(x,y,r,30*i*Math.PI/180,30*(i+1)*Math.PI/180,false); // 间隔为30度,弧度=角度*Math.PI/180
}
oGC.closePath();
oGC.stroke();
oGC.fillStyle ='white'; //覆盖住大刻度的黑色线
oGC.beginPath();
oGC.moveTo(x,y);
oGC.arc(x,y,r*18/20,360*(i+1)*Math.PI/180,false);
oGC.closePath();
oGC.fill();//表盘完成
oGC.lineWidth = 5;//设置时针宽度
oGC.beginPath();//开始绘制时针
oGC.moveTo(x,y);
oGC.arc(x,y,r*10/20,oHoursValue,oHoursValue,false);//设置时针大小和弧度
oGC.closePath();
oGC.stroke();
oGC.lineWidth = 3;//设置分针宽度
oGC.beginPath();//开始绘制分针
oGC.moveTo(x,y);
oGC.arc(x,y,r*14/20,oMinValue,oMinValue,false);//设置分针大小和弧度
oGC.closePath();
oGC.stroke();
oGC.lineWidth = 1;//设置秒针宽度
oGC.beginPath();//开始绘制秒针
oGC.moveTo(x,y);
oGC.arc(x,y,r*19/20,oSenValue,oSenValue,false);//设置秒针大小和弧度
oGC.closePath();
oGC.stroke();
}
setInterval(drawClock,1000);//设置定时器,让时钟运转起来
drawClock();
};
</script>
</head>
<body>
<canvas id = "ch1" width = "400px" height = "400px"></canvas>
</body>
</html>
点击下方result查看演示:www.szyoudun.com
你需要知道的:
canvas标签只是图形容器,您必须使用脚本来绘制图形。默认大小:宽300px,高150px;
getContext() 方法可返回一个对象,该对象提供了用于在画布上绘图的方法和属性。——获取上下文对象。
getContext("2d") 对象属性和方法,可用于在画布上绘制文本、线条、矩形、圆形等等。
fillRect(l,t,w,h):默认颜色是黑色 strokeRect(l,t,w,h):带边框的方块。默认一像素黑色边框
setInterval() 方法可按照指定的周期(以毫秒计)来调用函数或计算表达式。
beginPath():定义开始绘制路径, 它把当前的点设置为 (0,0)。 当一个画布的环境第一次创建,beginPath()
方法会被显式地调用。
closePath():结束绘制路径(将起点与终点进行连接)
绘制圆形:
arc( x,y,半径,起始弧度,结束弧度,旋转方向)
x,y:起始位置
弧度与角度的关系:弧度=角度*Math.PI/180
旋转方向:顺时针(默认:false,逆时针:true)
代码:
XML/HTML Code复制内容到剪贴板
<!DOCTYPE HTML>
<html lang="en-US">
<head>
<meta charset="UTF-8">
<title></title>
<script>
window.onload = function(){
var oC = document.getElementById('ch1');
var oGC = oC.getContext('2d');
function drawClock(){
var x = 200; //指定坐标
var y = 200;
var r = 150; //指定钟表半径
oGC.clearRect(0,0,oC.width,oC.height);//清空画布
var oDate = new Date(); //创建日期对象
var oHours = oDate.getHours();//获取时间
var oMin = oDate.getMinutes();
var oSen = oDate.getSeconds();
var oHoursValue = (-90 + oHours*30 + oMin/2)*Math.PI/180; //设置时针的值
var oMinValue = (-90 + oMin*6)*Math.PI/180;
var oSenValue = (-90 + oSen*6)*Math.PI/180;
oGC.beginPath();//开始
for(var i=0;i<60;i++){ //i为60,代表着时钟的60个小刻度
oGC.moveTo(x,y);
oGC.arc(x,y,r,6*i*Math.PI/180,6*(i+1)*Math.PI/180,false); //循环从6度到12度
}
oGC.closePath();
oGC.stroke();
oGC.fillStyle ='white'; //覆盖住小刻度的黑色线
oGC.beginPath();
oGC.moveTo(x,y);
oGC.arc(x,y,r*19/20,0,360*(i+1)*Math.PI/180,false);
oGC.closePath();//结束
oGC.fill();
oGC.lineWidth = 3; //设置时钟圆盘大刻度的粗细值
oGC.beginPath(); //开始画大的时钟刻度
for(i=0;i<12;i++){ //i为12,代表着时钟刻度的12大格
oGC.moveTo(x,y);
oGC.arc(x,y,r,30*i*Math.PI/180,30*(i+1)*Math.PI/180,false); // 间隔为30度,弧度=角度*Math.PI/180
}
oGC.closePath();
oGC.stroke();
oGC.fillStyle ='white'; //覆盖住大刻度的黑色线
oGC.beginPath();
oGC.moveTo(x,y);
oGC.arc(x,y,r*18/20,360*(i+1)*Math.PI/180,false);
oGC.closePath();
oGC.fill();//表盘完成
oGC.lineWidth = 5;//设置时针宽度
oGC.beginPath();//开始绘制时针
oGC.moveTo(x,y);
oGC.arc(x,y,r*10/20,oHoursValue,oHoursValue,false);//设置时针大小和弧度
oGC.closePath();
oGC.stroke();
oGC.lineWidth = 3;//设置分针宽度
oGC.beginPath();//开始绘制分针
oGC.moveTo(x,y);
oGC.arc(x,y,r*14/20,oMinValue,oMinValue,false);//设置分针大小和弧度
oGC.closePath();
oGC.stroke();
oGC.lineWidth = 1;//设置秒针宽度
oGC.beginPath();//开始绘制秒针
oGC.moveTo(x,y);
oGC.arc(x,y,r*19/20,oSenValue,oSenValue,false);//设置秒针大小和弧度
oGC.closePath();
oGC.stroke();
}
setInterval(drawClock,1000);//设置定时器,让时钟运转起来
drawClock();
};
</script>
</head>
<body>
<canvas id = "ch1" width = "400px" height = "400px"></canvas>
</body>
</html>
点击下方result查看演示:www.szyoudun.com
用HTML5.0制作网页的教程
根据你们询问的人,HTML 5不是迈向创造更语义化的网络的下一 个重要步伐,就是用一系列不完整的标签和标记大杂烩让网络陷入困境的灾难。
争论双方的问题在于,很少的站点在自然环境下使用HTML 5,所以现在所认识到的问题的理论解决方案仍然在很大程度上未经考验。
即便如此,我们不难发现下一代网络标记工具的好处和潜在的问题。
内容
- HTML 5有什么不同?
- 最终,一个任何人都可以记住的文档类型
- 最基本的语义结构
- <header>
- <nav>
- <section>
- <article>
- <aside>
- <footer>
- 把他们放在一起
- 为新元素编写样式
- 兼容老的浏览器
- 现在你可以使用HTML 5了,但你会用么?
HTML 5有什么不同?
首先,我们通过HTML 5表达什么?First off, what do we mean by HTML 5?理论上,我们表达所有的事——新的语义结构标签,例如canvas或者离线储存等API规范,以及新的内联语义标签。尽管如此,我们把实际的原因 (PS:浏览器支持问题)仅仅局限于结构标签。canvas,离线储存,本地视频或者地理定位API都很绝妙,然而他们还不能被所有浏览器一致的支持。
“但是等等”你说,“大多数浏览器也都不支持新的结构元素!”这是真的,但他们中的绝大多数将 会很乐意去接受你想要创建的任何标签。甚至连IE6也可以处理新标签,尽管如果你想要使用CSS设置样式,你需要一点JavaScript的帮助。
当你对新标签设置样式时,你需要记住一件事,那就是未知标签在大部分浏览器中没有默认样式。他 们同时被认为是行级元素。尽管如此,由于大部分HTML 5的新标签可以构造,我们将让他们拥有块级元素的行为。解决方法是确认你在CSS样式中包含了display:block;。
为了帮助了解当今HTML 5的一些新玩意儿,我们现在就进入正题,开始使用一些新的结构元素。
最终,一个任何人都可以记住的文档类型
我们创建HTML 5文档第一件需要做的事情就是使用新的文档类型。现在,如果你还清楚的记得HTML 4或者XHTML 1.x的文档类型,你真是一个比我们更强的淘气鬼。每当我们新建一个页面,我们必须打开一个旧的文件,剪切并粘贴文档类型定义。
这真是痛苦,也是为什么我们喜欢新的HTML 5文档类型。你准备好了么?他出现了:
不会太难记。简单并且容易理解。不区分大小写。
这个构想是停止HTML版本化,使向后兼容变得更容易。从长远看是否成功是另外的事情,但至少 他节省了你输入的平均时间。
最基本语义结构
我们已经将我们的页面定义为HTML 5文档。到现在为止,一切都还不错。现在,这些我们已经听说的新标签到底是什么?
在我们钻研新标签前,想想你一般网页的结构,大概像这样:
这对于展示用途很好,但如果我们想要知道一些关于页面元素包含什么的问题,这又怎么办呢?
上面的例子中,我们为我们所有的结构div添加了ID。这在有见识的设计师中是很平常的事。目 的有两个方面,首先,ID提供了可以能用于给页面的特殊段落应用样式的锚,其次,ID充当基本的伪语义结构。高明的解析器将会查看标签的ID属性,并尝试 去猜测他们的含义,但当每个站点的ID名称不同的时候很难。
这就是新结构标签到来的理由。
当认识到这些ID成为了惯例,HTML 5的缔造者们更进一步,使这些元素中的一部分变成他们独立的标签。这儿有一个HTML 5中生效的新标签的快速概要:
<header>
头部标签被设计作为关于一个章节或者一整张网页介绍信息的容器。<header> 标签可以包含从你位于大多数页面顶部的典型标志或者标语,到介绍一个章节的标语和开场白的任何东西。如果你还在你的页面里使用<div id=”header”>,那可以使用<header>替换
<nav>
nav元素非常明显,这是你的导航元素。当然什么被算为导航是有一些争议的,有一个基本的站点 导航,但一些情况下还可能有页面导航元素。HTML5的缔造者WHATWG最近在修改<nav>的解释,来表现怎样在同一个页面使用两次。
更多关于nav的信息以及关于HTML5的激烈争论,参见Jeffrey Zeldman关于nav元素的文章。
如果你还在使用<div id=”nav”>标签来包含你的页面导航,你可以使用简洁的<nav>标签来替换。
<section>
Section可能是新标签中最模糊的。根据HTML 5定义,一个章节是一个内容的主题集合,通常在header标签后,在footer标签前。但是如果 需要,section也可以相互嵌套。
在我们上面的例子里,被“content”标记的div就是一个变为section的很好的选 择。另外在那个section内,根据内容,我们可以增加section。
<article>
根绝WHATWG的注释,article元素可以包含“组成文档或站点独立部分的一段内容;例 如,杂志或者新闻的文章,或者博客条目。”
记住一个页面里可以有多个article标签;例如一个博客首页可能有最新的十篇文章,每一篇 包含在一个article标签内。Article也可以通过使用section标签分为多个段落,然而当你计划你的结构时需要稍微仔细一些,否则你容易引 起以一些难看的标签大杂烩结尾的情况。
<aside>
另一个相当模糊的标签,aside元素用于“与组成文档主要的正文流内容无关的”内容。那表示 一条附加的评论,内联的脚注,引用,注解或者像你看到的在这篇文章右边的更多典型的边栏内容。
根据WHATWG的注释,看起来<aside>可以用于所有的这些情况,尽管你边 栏里的引用和标签云有着很大的不同。
没人说HTML 5是完美的!
<footer>
Footer的用处也应该是很明显的,除了可能你不清楚可以拥有多个脚标。换句话说,除了通常 在大多数页面底部看到的主脚标,段落也可以含有脚标。
把他们放在一起
让我们使用新标签重新编写我们原来的例子:
非常清楚,并且容易理解,不是么?一些注释:我们可以在header标签中包含我们 的<h1>My Article</h1>标题。我没有这样做,因为h1元素已经表达了标题的含义,但如果你还有发布日期,署名或者其他数据在你文章的顶部, 为标签集添加一个header容器标签是一个很好的选择。
同时注意我们可以在article元素下添加第二个footer元素来包含诸如翻页导航,相关 文章或者其他内容。
为新标签编写样式
在大多数浏览器中,所有你需要做的就是像你通常做的那样,为在新标签上应用样式表,简单的定义 你的样式。但请确认为每一个元素添加了display:block;规则,无论如何,从现在开始。经过一段时间后,当浏览器开始标准化,并支持新元素后, 那就不必要了。
例如,让我们在我们的header里应用一些样式:
记住,你仍然可以给这些标签添加类和ID属性。所以,如果你想要单独为一个导航设置样式,你可 以轻易的给这个标签添加一个类或者样式,就象这样:
然后你可以应用一个样式:
兼容老的浏览器
但等一下,IE怎么办?这些样式完全不能在IE6下工作。如果你仍然需要支持像IE6一类遗产 般的浏览器,这儿有一个解决方法。IE6解析和显示这些标签还好,但你不能对他们设置任何CSS。解决方法是使用一点JavaScript。
我们只需要让IE去给我们使用createElement方法创造的的HTML 5标签设置样式。在HTML 5文件的head标签内添加这点东西。或者,你可以把他保存在一个特定的文件里,并用这种方法包含。
我知道你在想什么:“哥,你根本没有为那个脚本标签定义一个MIME类型。”
你根本不需要在HTML 5做这些事情。在HTML 5中,所有的脚本都被假定为type=”text/javascript”,所以没有必要让属性把你的脚本标签搞得乱七八糟(除非你的脚本并不是 JavaScript)。
这解决了IE的问题,但我们并没有摆脱困境。现在被证明Gecko渲染引擎有一个bug,导致 了Firefox2和Camino的一些版本在这些标签上卡住。
这儿有两个方法来处理这个bug,没有一个是理想的。更多的细节请查看HTML5doctor的这篇文章。这篇文章同时附有一个让所有HTML 5元素都生效的方便脚本。
记住,尽管Firefox 2的使用率很快在所有网站流量中降到了10%以下,但单纯忽略这个bug可能还是需要根据你网站的访问者来定。
现在你可以使用HTML 5了,但你会用么?
简短的回答是:我们会。
复杂一点的是:那要看站点了。如果你指责重新制作CNN主页,好吧,你可能会有一点抗拒,直到 浏览器的支持变好些。但如果你要给你的博客改版,我们支持你。这儿还有一些可以帮助你的Wordpress插 件,如果你正在使用这么流行的发布系统。这儿是一个Jeff Starr制作的HTML 5主题。
同时,试试以站点为主的HTML 5美术馆,并且查看源代码,看看他们做了什么。
尽管如此,如果IE的缺点阻止你了,这样考虑吧:就连Google也在他们的主要搜索页面上使 用了HTML 5的文档类型。就算如果你不使用所有新的结构标签,你可以至少利用一下简洁的脚本声明和下次我们会介绍的关于一些非结构的语义标签。www.szyoudun.com
根据你们询问的人,HTML 5不是迈向创造更语义化的网络的下一 个重要步伐,就是用一系列不完整的标签和标记大杂烩让网络陷入困境的灾难。
争论双方的问题在于,很少的站点在自然环境下使用HTML 5,所以现在所认识到的问题的理论解决方案仍然在很大程度上未经考验。
即便如此,我们不难发现下一代网络标记工具的好处和潜在的问题。
内容
- HTML 5有什么不同?
- 最终,一个任何人都可以记住的文档类型
- 最基本的语义结构
- <header>
- <nav>
- <section>
- <article>
- <aside>
- <footer>
- 把他们放在一起
- 为新元素编写样式
- 兼容老的浏览器
- 现在你可以使用HTML 5了,但你会用么?
HTML 5有什么不同?
首先,我们通过HTML 5表达什么?First off, what do we mean by HTML 5?理论上,我们表达所有的事——新的语义结构标签,例如canvas或者离线储存等API规范,以及新的内联语义标签。尽管如此,我们把实际的原因 (PS:浏览器支持问题)仅仅局限于结构标签。canvas,离线储存,本地视频或者地理定位API都很绝妙,然而他们还不能被所有浏览器一致的支持。
“但是等等”你说,“大多数浏览器也都不支持新的结构元素!”这是真的,但他们中的绝大多数将 会很乐意去接受你想要创建的任何标签。甚至连IE6也可以处理新标签,尽管如果你想要使用CSS设置样式,你需要一点JavaScript的帮助。
当你对新标签设置样式时,你需要记住一件事,那就是未知标签在大部分浏览器中没有默认样式。他 们同时被认为是行级元素。尽管如此,由于大部分HTML 5的新标签可以构造,我们将让他们拥有块级元素的行为。解决方法是确认你在CSS样式中包含了display:block;。
为了帮助了解当今HTML 5的一些新玩意儿,我们现在就进入正题,开始使用一些新的结构元素。
最终,一个任何人都可以记住的文档类型
我们创建HTML 5文档第一件需要做的事情就是使用新的文档类型。现在,如果你还清楚的记得HTML 4或者XHTML 1.x的文档类型,你真是一个比我们更强的淘气鬼。每当我们新建一个页面,我们必须打开一个旧的文件,剪切并粘贴文档类型定义。
这真是痛苦,也是为什么我们喜欢新的HTML 5文档类型。你准备好了么?他出现了:
不会太难记。简单并且容易理解。不区分大小写。
这个构想是停止HTML版本化,使向后兼容变得更容易。从长远看是否成功是另外的事情,但至少 他节省了你输入的平均时间。
最基本语义结构
我们已经将我们的页面定义为HTML 5文档。到现在为止,一切都还不错。现在,这些我们已经听说的新标签到底是什么?
在我们钻研新标签前,想想你一般网页的结构,大概像这样:
这对于展示用途很好,但如果我们想要知道一些关于页面元素包含什么的问题,这又怎么办呢?
上面的例子中,我们为我们所有的结构div添加了ID。这在有见识的设计师中是很平常的事。目 的有两个方面,首先,ID提供了可以能用于给页面的特殊段落应用样式的锚,其次,ID充当基本的伪语义结构。高明的解析器将会查看标签的ID属性,并尝试 去猜测他们的含义,但当每个站点的ID名称不同的时候很难。
这就是新结构标签到来的理由。
当认识到这些ID成为了惯例,HTML 5的缔造者们更进一步,使这些元素中的一部分变成他们独立的标签。这儿有一个HTML 5中生效的新标签的快速概要:
<header>
头部标签被设计作为关于一个章节或者一整张网页介绍信息的容器。<header> 标签可以包含从你位于大多数页面顶部的典型标志或者标语,到介绍一个章节的标语和开场白的任何东西。如果你还在你的页面里使用<div id=”header”>,那可以使用<header>替换
<nav>
nav元素非常明显,这是你的导航元素。当然什么被算为导航是有一些争议的,有一个基本的站点 导航,但一些情况下还可能有页面导航元素。HTML5的缔造者WHATWG最近在修改<nav>的解释,来表现怎样在同一个页面使用两次。
更多关于nav的信息以及关于HTML5的激烈争论,参见Jeffrey Zeldman关于nav元素的文章。
如果你还在使用<div id=”nav”>标签来包含你的页面导航,你可以使用简洁的<nav>标签来替换。
<section>
Section可能是新标签中最模糊的。根据HTML 5定义,一个章节是一个内容的主题集合,通常在header标签后,在footer标签前。但是如果 需要,section也可以相互嵌套。
在我们上面的例子里,被“content”标记的div就是一个变为section的很好的选 择。另外在那个section内,根据内容,我们可以增加section。
<article>
根绝WHATWG的注释,article元素可以包含“组成文档或站点独立部分的一段内容;例 如,杂志或者新闻的文章,或者博客条目。”
记住一个页面里可以有多个article标签;例如一个博客首页可能有最新的十篇文章,每一篇 包含在一个article标签内。Article也可以通过使用section标签分为多个段落,然而当你计划你的结构时需要稍微仔细一些,否则你容易引 起以一些难看的标签大杂烩结尾的情况。
<aside>
另一个相当模糊的标签,aside元素用于“与组成文档主要的正文流内容无关的”内容。那表示 一条附加的评论,内联的脚注,引用,注解或者像你看到的在这篇文章右边的更多典型的边栏内容。
根据WHATWG的注释,看起来<aside>可以用于所有的这些情况,尽管你边 栏里的引用和标签云有着很大的不同。
没人说HTML 5是完美的!
<footer>
Footer的用处也应该是很明显的,除了可能你不清楚可以拥有多个脚标。换句话说,除了通常 在大多数页面底部看到的主脚标,段落也可以含有脚标。
把他们放在一起
让我们使用新标签重新编写我们原来的例子:
非常清楚,并且容易理解,不是么?一些注释:我们可以在header标签中包含我们 的<h1>My Article</h1>标题。我没有这样做,因为h1元素已经表达了标题的含义,但如果你还有发布日期,署名或者其他数据在你文章的顶部, 为标签集添加一个header容器标签是一个很好的选择。
同时注意我们可以在article元素下添加第二个footer元素来包含诸如翻页导航,相关 文章或者其他内容。
为新标签编写样式
在大多数浏览器中,所有你需要做的就是像你通常做的那样,为在新标签上应用样式表,简单的定义 你的样式。但请确认为每一个元素添加了display:block;规则,无论如何,从现在开始。经过一段时间后,当浏览器开始标准化,并支持新元素后, 那就不必要了。
例如,让我们在我们的header里应用一些样式:
记住,你仍然可以给这些标签添加类和ID属性。所以,如果你想要单独为一个导航设置样式,你可 以轻易的给这个标签添加一个类或者样式,就象这样:
然后你可以应用一个样式:
兼容老的浏览器
但等一下,IE怎么办?这些样式完全不能在IE6下工作。如果你仍然需要支持像IE6一类遗产 般的浏览器,这儿有一个解决方法。IE6解析和显示这些标签还好,但你不能对他们设置任何CSS。解决方法是使用一点JavaScript。
我们只需要让IE去给我们使用createElement方法创造的的HTML 5标签设置样式。在HTML 5文件的head标签内添加这点东西。或者,你可以把他保存在一个特定的文件里,并用这种方法包含。
我知道你在想什么:“哥,你根本没有为那个脚本标签定义一个MIME类型。”
你根本不需要在HTML 5做这些事情。在HTML 5中,所有的脚本都被假定为type=”text/javascript”,所以没有必要让属性把你的脚本标签搞得乱七八糟(除非你的脚本并不是 JavaScript)。
这解决了IE的问题,但我们并没有摆脱困境。现在被证明Gecko渲染引擎有一个bug,导致 了Firefox2和Camino的一些版本在这些标签上卡住。
这儿有两个方法来处理这个bug,没有一个是理想的。更多的细节请查看HTML5doctor的这篇文章。这篇文章同时附有一个让所有HTML 5元素都生效的方便脚本。
记住,尽管Firefox 2的使用率很快在所有网站流量中降到了10%以下,但单纯忽略这个bug可能还是需要根据你网站的访问者来定。
现在你可以使用HTML 5了,但你会用么?
简短的回答是:我们会。
复杂一点的是:那要看站点了。如果你指责重新制作CNN主页,好吧,你可能会有一点抗拒,直到 浏览器的支持变好些。但如果你要给你的博客改版,我们支持你。这儿还有一些可以帮助你的Wordpress插 件,如果你正在使用这么流行的发布系统。这儿是一个Jeff Starr制作的HTML 5主题。
同时,试试以站点为主的HTML 5美术馆,并且查看源代码,看看他们做了什么。
尽管如此,如果IE的缺点阻止你了,这样考虑吧:就连Google也在他们的主要搜索页面上使 用了HTML 5的文档类型。就算如果你不使用所有新的结构标签,你可以至少利用一下简洁的脚本声明和下次我们会介绍的关于一些非结构的语义标签。www.szyoudun.com
基于HTML5的云虚拟主机配置界面
云计算大行其道,上期开源中国的原创会就有好几位云计算专家演讲,从底层的虚拟化技术,到上层的云存储和应用API,耳濡目染,也受益匪浅,算是大势所趋,回头看看Qunee组件,借这个趋势,可以在云计算可视化上发挥作用,最近就有客户用Qunee实现VPC配置图,并对交互做了定制,细节不便多说,本文主要介绍Qunee交互扩展方面的思路
云计算大行其道,上期开源中国的原创会就有好几位云计算专家演讲,从底层的虚拟化技术,到上层的云存储和应用API,耳濡目染,也受益匪浅,算是大势所趋,回头看看Qunee组件,借这个趋势,可以在云计算可视化上发挥作用,最近就有客户用Qunee实现VPC配置图,并对交互做了定制,细节不便多说,本文主要介绍Qunee交互扩展方面的思路
VPC配置图
参考示例 – visualops
云平台可视化这块儿国外做的不错,有不少开放的示例可以学习和参考,客户看中了这家云管理系统http://www.visualops.io/ ,这个系统使用的是SVG技术,体验了一下,效果不错,参照着实现
从界面上看,一个云节点上有多个子网,每个子网内有多台虚拟主机,然后每个云节点有统一的路由Router管理(或者网关Gateway)
visualops
界面呈现
参照原型实现,使用Q.Path描出子网和云节点范围,虚拟机则用普通节点,连线使用正交连线类型,此外还有些外观配置,比如虚线,背景颜色,网格等,设置style或者css即可
创建矩形元素(子网、云节点)
function createRect(name, left, top, width, height, fillColor) {
var rect = graph.createNode(name, left, top);
rect.type = "rectGroup";
rect.anchorPosition = Q.Position.LEFT_TOP;
rect.image = Q.Shapes.getRect(0, 0, 100, 100);
rect.size = {width: width, height: height};
rect.setStyle(Q.Styles.IMAGE_BACKGROUND_COLOR, fillColor || Q.toColor(0x88FFFFFF));
rect.setStyle(Q.Styles.SHAPE_STROKE, 0);
rect.setStyle(Q.Styles.BORDER, 1);
rect.setStyle(Q.Styles.BORDER_COLOR, "#32c02f");
rect.setStyle(Q.Styles.BORDER_LINE_DASH, [5, 6]);
rect.setStyle(Q.Styles.LABEL_PADDING, 5);
rect.setStyle(Q.Styles.LABEL_ANCHOR_POSITION, Q.Position.LEFT_TOP);
rect.setStyle(Q.Styles.LABEL_POSITION, Q.Position.LEFT_TOP);
rect.setBounds = function (bounds) {
this.x = bounds.x;
this.y = bounds.y;
this.size = new Q.Size(bounds.width, bounds.height);
}
rect.getBounds = function () {
return new Q.Rect(this.x, this.y, this.size.width, this.size.height);
}
rect.inBounds = function (x, y) {
return this.getBounds().contains(x, y);
}
rect.intersectsRect = function (rect) {
return rect.intersectsRect(this.x, this.y, this.size.width, this.size.height);
}
rect.containsRect = function (rect) {
return Q.containsRect(this.x, this.y, this.size.width, this.size.height, rect.x, rect.y, rect.width, rect.height);
}
rect.updateBoundsByChildren = function () {
var bounds = new Q.Rect();
Q.forEachChild(this, function (child) {
bounds.add(graph.getUIBounds(child));
});
this.location = new Q.Point(bounds.x, bounds.y - 15);
this.size = new Q.Size(Math.max(200, bounds.width), Math.max(70, bounds.height + 15));
}
return rect;
}
注意到对Rect增加了一些标定节点范围、计算自身大小的方法,后面交互时会用到
创建虚拟主机
function createPC(name, x, y, parent) {
var node = graph.createNode(name, x, y);
node.image = Q.Graphs.server;
node.parent = node.host = parent;
node.zIndex = 10;
return node;
}
创建连接关系
function createEdge(from, to) {
var name = from.name + "->" + to.name;
var edge = graph.createEdge(name, from, to);
edge.edgeType = Q.Consts.EDGE_TYPE_VERTICAL_HORIZONTAL;
edge.setStyle(Q.Styles.EDGE_FROM_OFFSET, {x: 20});
edge.setStyle(Q.Styles.EDGE_CORNER_RADIUS, 0);
return edge;
}
定制交互
呈现出来不难,业务的体现在于交互,客户需求要求虚拟主机不得超出云节点范围,超出时需要回撤操作,此外可以将一台虚拟机从一个子网移到另一个子网,另外还要控制路由的移动范围,限定在云节点的左边框上……
实现思路是通过交互监听,分别对移动(ELEMENT_MOVING),调整大小(RESIZING)等情况做处理,Qunee交互事件通常有三个状态:开始、进行时、结束,比如移动交互的三个过程:开始移动(ELEMENT_MOVE_START),移动中(ELEMENT_MOVING),完成移动(ELEMENT_MOVE_END),我们可以在开始时记录节点原始位置,结束时判断节点位置是否允许,如果不允许则作调整
var oldLocation = {};
graph.interactionDispatcher.addListener(function (evt) {
var data = evt.data;
if (evt.kind == Q.InteractionEvent.RESIZE_START) {
oldLocation[data.id] = {x: data.x, y: data.y, width: data.size.width, height: data.size.height};
}
if (evt.kind == Q.InteractionEvent.RESIZING) {
if (data == mainNode) {
label.updateLocation();
}
return;
}
if (evt.kind == Q.InteractionEvent.RESIZE_END) {
var oldBounds = oldLocation[data.id];
oldLocation = {};
if ((data != mainNode && isGroup(data) && groupIntersected(data)) || childrenOutParent(data)) {
data.setBounds(oldBounds);
}
if (data == mainNode) {
label.updateLocation();
}
return;
}
if (data.atLeft) {
if (evt.kind == Q.InteractionEvent.ELEMENT_MOVING) {
data.updateLocation();
}
return;
}
if (evt.kind == Q.InteractionEvent.ELEMENT_MOVE_START) {
oldLocation[data.id] = {x: data.x, y: data.y};
return;
}
if (evt.kind == Q.InteractionEvent.ELEMENT_MOVING) {
if (!isGroup(data)) {
var g = findGroup(evt.event);
highlight(g);
}
return;
}
if (evt.kind == Q.InteractionEvent.ELEMENT_MOVE_END) {
highlight();
if (isGroup(data)) {
if (groupIntersected(data)) {
var old = oldLocation[data.id];
graph.moveElements([data], old.x - data.x, old.y - data.y);
}
return;
}
var g = findGroup(evt.event);
if (g) {
data.parent = data.host = g;
fixNode(data);
} else {
data.location = oldLocation[data.id];
}
oldLocation = {};
}
})
最终效果
VPC配置图www.szyoudun.com
云计算大行其道,上期开源中国的原创会就有好几位云计算专家演讲,从底层的虚拟化技术,到上层的云存储和应用API,耳濡目染,也受益匪浅,算是大势所趋,回头看看Qunee组件,借这个趋势,可以在云计算可视化上发挥作用,最近就有客户用Qunee实现VPC配置图,并对交互做了定制,细节不便多说,本文主要介绍Qunee交互扩展方面的思路
云计算大行其道,上期开源中国的原创会就有好几位云计算专家演讲,从底层的虚拟化技术,到上层的云存储和应用API,耳濡目染,也受益匪浅,算是大势所趋,回头看看Qunee组件,借这个趋势,可以在云计算可视化上发挥作用,最近就有客户用Qunee实现VPC配置图,并对交互做了定制,细节不便多说,本文主要介绍Qunee交互扩展方面的思路
VPC配置图
参考示例 – visualops
云平台可视化这块儿国外做的不错,有不少开放的示例可以学习和参考,客户看中了这家云管理系统http://www.visualops.io/ ,这个系统使用的是SVG技术,体验了一下,效果不错,参照着实现
从界面上看,一个云节点上有多个子网,每个子网内有多台虚拟主机,然后每个云节点有统一的路由Router管理(或者网关Gateway)
visualops
界面呈现
参照原型实现,使用Q.Path描出子网和云节点范围,虚拟机则用普通节点,连线使用正交连线类型,此外还有些外观配置,比如虚线,背景颜色,网格等,设置style或者css即可
创建矩形元素(子网、云节点)
function createRect(name, left, top, width, height, fillColor) {
var rect = graph.createNode(name, left, top);
rect.type = "rectGroup";
rect.anchorPosition = Q.Position.LEFT_TOP;
rect.image = Q.Shapes.getRect(0, 0, 100, 100);
rect.size = {width: width, height: height};
rect.setStyle(Q.Styles.IMAGE_BACKGROUND_COLOR, fillColor || Q.toColor(0x88FFFFFF));
rect.setStyle(Q.Styles.SHAPE_STROKE, 0);
rect.setStyle(Q.Styles.BORDER, 1);
rect.setStyle(Q.Styles.BORDER_COLOR, "#32c02f");
rect.setStyle(Q.Styles.BORDER_LINE_DASH, [5, 6]);
rect.setStyle(Q.Styles.LABEL_PADDING, 5);
rect.setStyle(Q.Styles.LABEL_ANCHOR_POSITION, Q.Position.LEFT_TOP);
rect.setStyle(Q.Styles.LABEL_POSITION, Q.Position.LEFT_TOP);
rect.setBounds = function (bounds) {
this.x = bounds.x;
this.y = bounds.y;
this.size = new Q.Size(bounds.width, bounds.height);
}
rect.getBounds = function () {
return new Q.Rect(this.x, this.y, this.size.width, this.size.height);
}
rect.inBounds = function (x, y) {
return this.getBounds().contains(x, y);
}
rect.intersectsRect = function (rect) {
return rect.intersectsRect(this.x, this.y, this.size.width, this.size.height);
}
rect.containsRect = function (rect) {
return Q.containsRect(this.x, this.y, this.size.width, this.size.height, rect.x, rect.y, rect.width, rect.height);
}
rect.updateBoundsByChildren = function () {
var bounds = new Q.Rect();
Q.forEachChild(this, function (child) {
bounds.add(graph.getUIBounds(child));
});
this.location = new Q.Point(bounds.x, bounds.y - 15);
this.size = new Q.Size(Math.max(200, bounds.width), Math.max(70, bounds.height + 15));
}
return rect;
}
注意到对Rect增加了一些标定节点范围、计算自身大小的方法,后面交互时会用到
创建虚拟主机
function createPC(name, x, y, parent) {
var node = graph.createNode(name, x, y);
node.image = Q.Graphs.server;
node.parent = node.host = parent;
node.zIndex = 10;
return node;
}
创建连接关系
function createEdge(from, to) {
var name = from.name + "->" + to.name;
var edge = graph.createEdge(name, from, to);
edge.edgeType = Q.Consts.EDGE_TYPE_VERTICAL_HORIZONTAL;
edge.setStyle(Q.Styles.EDGE_FROM_OFFSET, {x: 20});
edge.setStyle(Q.Styles.EDGE_CORNER_RADIUS, 0);
return edge;
}
定制交互
呈现出来不难,业务的体现在于交互,客户需求要求虚拟主机不得超出云节点范围,超出时需要回撤操作,此外可以将一台虚拟机从一个子网移到另一个子网,另外还要控制路由的移动范围,限定在云节点的左边框上……
实现思路是通过交互监听,分别对移动(ELEMENT_MOVING),调整大小(RESIZING)等情况做处理,Qunee交互事件通常有三个状态:开始、进行时、结束,比如移动交互的三个过程:开始移动(ELEMENT_MOVE_START),移动中(ELEMENT_MOVING),完成移动(ELEMENT_MOVE_END),我们可以在开始时记录节点原始位置,结束时判断节点位置是否允许,如果不允许则作调整
var oldLocation = {};
graph.interactionDispatcher.addListener(function (evt) {
var data = evt.data;
if (evt.kind == Q.InteractionEvent.RESIZE_START) {
oldLocation[data.id] = {x: data.x, y: data.y, width: data.size.width, height: data.size.height};
}
if (evt.kind == Q.InteractionEvent.RESIZING) {
if (data == mainNode) {
label.updateLocation();
}
return;
}
if (evt.kind == Q.InteractionEvent.RESIZE_END) {
var oldBounds = oldLocation[data.id];
oldLocation = {};
if ((data != mainNode && isGroup(data) && groupIntersected(data)) || childrenOutParent(data)) {
data.setBounds(oldBounds);
}
if (data == mainNode) {
label.updateLocation();
}
return;
}
if (data.atLeft) {
if (evt.kind == Q.InteractionEvent.ELEMENT_MOVING) {
data.updateLocation();
}
return;
}
if (evt.kind == Q.InteractionEvent.ELEMENT_MOVE_START) {
oldLocation[data.id] = {x: data.x, y: data.y};
return;
}
if (evt.kind == Q.InteractionEvent.ELEMENT_MOVING) {
if (!isGroup(data)) {
var g = findGroup(evt.event);
highlight(g);
}
return;
}
if (evt.kind == Q.InteractionEvent.ELEMENT_MOVE_END) {
highlight();
if (isGroup(data)) {
if (groupIntersected(data)) {
var old = oldLocation[data.id];
graph.moveElements([data], old.x - data.x, old.y - data.y);
}
return;
}
var g = findGroup(evt.event);
if (g) {
data.parent = data.host = g;
fixNode(data);
} else {
data.location = oldLocation[data.id];
}
oldLocation = {};
}
})
最终效果
VPC配置图www.szyoudun.com
收起阅读 »php 建造者生成器模式学习笔记
适用场景:
1、 需要生成的产品对象有复杂的内部结构。
2、 需要生成的产品对象的属性相互依赖,生成器模式可以强迫生成顺序。
3、 在对象创建过程中会使用到系统中的一些其它对象,这些对象在产品对象的创建过程中不易得到。
使用效果:
1、 生成器模式的使用使得产品的内部表象可以独立的变化。使用生成器模式可以使客户端不必知道产品内部组成的细节。
2、 每一个Builder都相对独立,而与其它的Builder无关。
3、 模式所建造的最终产品更易于控制。
<?php
/**
- Created by PhpStorm.
*/
/**具体产品角色 车
- Class Car
*/
class Car
{
public $_head;
public $_engine;//引擎
public $_tyre;//轮胎
function show()
{
echo "车头的颜色:{$this->_head}<br/>";
echo "引擎的品牌:{$this->_engine}<br/>";
echo "轮胎的品牌:{$this->_tyre}<br/>";
}
}
/**抽象车的建造者(生成器)
- Class CarBuilder
*/
abstract class CarBuilder
{
protected $_car;
function __construct()
{
$this->_car=new Car();
}
abstract function BuildHead();
abstract function BuildEngine();
abstract function BuildTyre();
abstract function GetCar();
}
/**具体车的建造者(生成器) 宝马
- Class BMW
*/
class BMW extends CarBuilder
{
function BuildHead()
{
// TODO: Implement BuilderHead() method.
$this->_car->_head="Black";
}
function BuildEngine()
{
// TODO: Implement BuildEngine() method.
$this->_car->_engine="BMW";
}
function BuildTyre()
{
// TODO: Implement BuildTyre() method.
$this->_car->_tyre="BMW";
}
function GetCar()
{
// TODO: Implement GetCar() method.
return $this->_car;
}
}
/**别克
- Class BuickBird
*/
class BuickBird extends CarBuilder
{
function BuildHead()
{
// TODO: Implement BuildHead() method.
$this->_car->_head="Red";
}
function BuildEngine()
{
// TODO: Implement BuildEngine() BMmethod.
$this->_car->_engine="Buick";//
}
function BuildTyre()
{
// TODO: Implement BuildTyre() method.
$this->_car->_tyre="Buick";
}
function GetCar()
{
// TODO: Implement GetCar() method.
return $this->_car;
}
}
/**指挥者
- Class Director
*/
class Director
{
/** - @param $_builder 建造者
- @return mixed 产品类:车
*/
function Construct($_builder)
{
$_builder->BuildHead();
$_builder->BuildEngine();
$_builder->BuildTyre();
return $_builder->GetCar();
}
}www.szyoudun.com
适用场景:
1、 需要生成的产品对象有复杂的内部结构。
2、 需要生成的产品对象的属性相互依赖,生成器模式可以强迫生成顺序。
3、 在对象创建过程中会使用到系统中的一些其它对象,这些对象在产品对象的创建过程中不易得到。
使用效果:
1、 生成器模式的使用使得产品的内部表象可以独立的变化。使用生成器模式可以使客户端不必知道产品内部组成的细节。
2、 每一个Builder都相对独立,而与其它的Builder无关。
3、 模式所建造的最终产品更易于控制。
<?php
/**
- Created by PhpStorm.
*/
/**具体产品角色 车
- Class Car
*/
class Car
{
public $_head;
public $_engine;//引擎
public $_tyre;//轮胎
function show()
{
echo "车头的颜色:{$this->_head}<br/>";
echo "引擎的品牌:{$this->_engine}<br/>";
echo "轮胎的品牌:{$this->_tyre}<br/>";
}
}
/**抽象车的建造者(生成器)
- Class CarBuilder
*/
abstract class CarBuilder
{
protected $_car;
function __construct()
{
$this->_car=new Car();
}
abstract function BuildHead();
abstract function BuildEngine();
abstract function BuildTyre();
abstract function GetCar();
}
/**具体车的建造者(生成器) 宝马
- Class BMW
*/
class BMW extends CarBuilder
{
function BuildHead()
{
// TODO: Implement BuilderHead() method.
$this->_car->_head="Black";
}
function BuildEngine()
{
// TODO: Implement BuildEngine() method.
$this->_car->_engine="BMW";
}
function BuildTyre()
{
// TODO: Implement BuildTyre() method.
$this->_car->_tyre="BMW";
}
function GetCar()
{
// TODO: Implement GetCar() method.
return $this->_car;
}
}
/**别克
- Class BuickBird
*/
class BuickBird extends CarBuilder
{
function BuildHead()
{
// TODO: Implement BuildHead() method.
$this->_car->_head="Red";
}
function BuildEngine()
{
// TODO: Implement BuildEngine() BMmethod.
$this->_car->_engine="Buick";//
}
function BuildTyre()
{
// TODO: Implement BuildTyre() method.
$this->_car->_tyre="Buick";
}
function GetCar()
{
// TODO: Implement GetCar() method.
return $this->_car;
}
}
/**指挥者
- Class Director
*/
class Director
{
/** - @param $_builder 建造者
- @return mixed 产品类:车
*/
function Construct($_builder)
{
$_builder->BuildHead();
$_builder->BuildEngine();
$_builder->BuildTyre();
return $_builder->GetCar();
}
}www.szyoudun.com
php反射类 ReflectionClass使用例子
反射类是一个类的映射
namespace News;
class News{
public $newsid;
public function index(){
……
}
}
正常我们实例化一个类是这样: $News = new News\News();
如果我们要实例化News类的反射类是这样:$News = new \ReflectionClass(‘News\News’);
那么通过反射类实例化的类和普通类有什么不同呢?
通过反射类实例化的类我们可以获取这个这个类的详细信息,可以对类进行分析
例如:$News->getProperties()可以获取这个类的所有属性与方法。
array (size=1)
0 => & object(ReflectionProperty)[2]
public 'name' => string 'newsid' (length=6)
public 'class' => string 'News' (length=4)
getShortName() getName() getNamespaceName() 分别可以获得类名,带命名空间的类名,和命名空间的名称 News News\News News
ReflectionClass的一个应用就是在父类中获取子类的名称,从而针对不同的子类做不同的处理:
$reflection = new \ReflectionClass($this);
$class_name = $reflection->getShortName();
// check key
if (!key_exists($class_name, $this->configs)) {
return true;
}
// params
$params = $this->configs[$class_name];
return $this->_handle($params);
反射类的使用应该可以相当灵活,还有相当多的其他用途待发掘。www.qqfarms.cn
反射类是一个类的映射
namespace News;
class News{
public $newsid;
public function index(){
……
}
}
正常我们实例化一个类是这样: $News = new News\News();
如果我们要实例化News类的反射类是这样:$News = new \ReflectionClass(‘News\News’);
那么通过反射类实例化的类和普通类有什么不同呢?
通过反射类实例化的类我们可以获取这个这个类的详细信息,可以对类进行分析
例如:$News->getProperties()可以获取这个类的所有属性与方法。
array (size=1)
0 => & object(ReflectionProperty)[2]
public 'name' => string 'newsid' (length=6)
public 'class' => string 'News' (length=4)
getShortName() getName() getNamespaceName() 分别可以获得类名,带命名空间的类名,和命名空间的名称 News News\News News
ReflectionClass的一个应用就是在父类中获取子类的名称,从而针对不同的子类做不同的处理:
$reflection = new \ReflectionClass($this);
$class_name = $reflection->getShortName();
// check key
if (!key_exists($class_name, $this->configs)) {
return true;
}
// params
$params = $this->configs[$class_name];
return $this->_handle($params);
反射类的使用应该可以相当灵活,还有相当多的其他用途待发掘。www.qqfarms.cn
PHP中简单工厂模式的学习笔记
简单工厂模式:
①抽象基类:类中定义抽象一些方法,用以在子类中实现
②继承自抽象基类的子类:实现基类中的抽象方法
③工厂类:用以实例化对象
使用工厂模式的目的或目标?
工厂模式的最大优点在于创建对象上面,就是把创建对象的过程封装起来,这样随时可以产生一个新的对象。
减少代码进行复制粘帖,耦合关系重,牵一发动其他部分代码。
通俗的说,以前创建一个对象要使用new,现在把这个过程封装起来了。
假设不使用工厂模式:那么很多地方调用类a,代码就会这样子创建一个实例:new a(),假设某天需要把a类的名称修改,意味着很多调用的代码都要修改。
工厂模式的优点就在创建对象上。
工厂模式的优点就在创建对象上。建立一个工厂(一个函数或一个类方法)来制造新的对象,它的任务就是把对象的创建过程都封装起来,
创建对象不是使用new的形式了。而是定义一个方法,用于创建对象实例。
每个类可能会需要连接数据库。那么就将连接数据库封装在一个类中。以后在其他类中通过类名:
为什么引入抽象的概念?
想一想,在现实生活中,当我们无法确定某个具体的东西的时候,往往把一类东西归于抽象类别。
工厂方法:
比如你的工厂叫做“香烟工厂”,那么可以有“七匹狼工厂”“中华工厂”等,但是,这个工厂只生厂一种商品:香烟;
抽象工厂:无法描述它到底生产什么产品,它生产很多类型的产品(所以抽象工厂就会生成子工厂)。
你的工厂是综合型的,是生产“一系列”产品,而不是“一个”,比如:生产“香烟”,还有“啤酒”等。然后它也可以有派生出来的具体的工厂,但这些工厂都是生产这一系列产品,只是可能因为地域不一样,为了适应当地人口味,味道也不太一样。
工厂模式:理解成只生成一种产品的工厂。比如生产香烟的。
工厂方法:工厂的一种产品生产线 。比如键盘的生成过程。
别人会反驳:吃饱了没事干,一定要修改类名称呢?这个说不定。一般都不会去修改类名称。
其实工厂模式有很多变体,抓住精髓才是关键:只要是可以根据不同的参数生成不同的类实例,那么就符合工厂模式的设计思想。
这样子让我联想到框架中经常会有负责生成具体类实例的方法供调用。
看完文章再回头来看下这张图,效果会比较好
采用封装方式
<?php
class Calc{
/**
- 计算结果
- @param int|float $num1
- @param int|float $num2
- @param string $operator
- @return int|float
/
public function calculate($num1,$num2,$operator){
try {
$result=0;
switch ($operator){
case '+':
$result= $num1+$num2;
break;
case '-':
$result= $num1-$num2;
break;
case '':
$result= $num1*$num2;
break;
case '/':
if ($num2==0) {
throw new Exception("除数不能为0");
}
$result= $num1/$num2;
break;
}
return $result;
}catch (Exception $e){
echo "您输入有误:".$e->getMessage();
}
}
}
$test=new Calc();
// echo $test->calculate(2,3,'+');//打印:5
echo $test->calculate(5,0,'/');//打印:您输入有误:除数不能为0
?>
优点:以上代码使用了面向对象的封装特性,只要有了include这个类,其他页面就可以随便使用了
缺点:无法灵活的扩展和维护
比如:想要增加一个“求余”运算,需要在switch语句块中添加一个分支语句,代码需要做如下改动
<?php
class Calc{
public function calculate($num1,$num2,$operator){
try {
$result=0;
switch ($operator){
//......省略......
case '%':
$result= $num1%$num2;
break;
//......省略......
}
}catch (Exception $e){
echo "您输入有误:".$e->getMessage();
}
}
}
?>
代码分析:用以上方法实现给计算器添加新的功能运算有以下几个缺点
①需要改动原有的代码块,可能会在为了“添加新功能”而改动原有代码的时候,不小心将原有的代码改错了
②如果要添加的功能很多,比如:‘乘方’,‘开方’,‘对数’,‘三角函数’,‘统计’,或者添加一些程序员专用的计算功能,比如:And, Or, Not, Xor,这样就需要在switch语句中添加N个分支语句。想象下,一个计算功能的函数如果有二三十个case分支语句,代码将超过一屏,不仅令代码的可读性大大降低,关键是,为了添加小功能,还得让其余不相关都参与解释,这令程序的执行效率大大降低
解决途径:采用OOP的继承和多态思想
<?php
/**
- 操作类
- 因为包含有抽象方法,所以类必须声明为抽象类
*/
abstract class Operation{
//抽象方法不能包含函数体
abstract public function getValue($num1,$num2);//强烈要求子类必须实现该功能函数
}
/** - 加法类
*/
class OperationAdd extends Operation {
public function getValue($num1,$num2){
return $num1+$num2;
}
}
/** - 减法类
*/
class OperationSub extends Operation {
public function getValue($num1,$num2){
return $num1-$num2;
}
}
/** - 乘法类
/
class OperationMul extends Operation {
public function getValue($num1,$num2){
return $num1$num2;
}
}
/** - 除法类
*/
class OperationDiv extends Operation {
public function getValue($num1,$num2){
try {
if ($num2==0){
throw new Exception("除数不能为0");
}else {
return $num1/$num2;
}
}catch (Exception $e){
echo "错误信息:".$e->getMessage();
}
}
}
?>
这里采用了面向对象的继承特性,首先声明一个虚拟基类,在基类中指定子类务必实现的方法(getValue())
分析:通过采用面向对象的继承特性,我们可以很容易就能对原有程序进行扩展,比如:‘乘方’,‘开方’,‘对数’,‘三角函数’,‘统计’等等。
<?php
/**
- 求余类(remainder)
-
*/
class OperationRem extends Operation {
public function getValue($num1,$num2){
return $num1%$num12;
}
}
?>
我们只需要另外写一个类(该类继承虚拟基类),在类中完成相应的功能(比如:求乘方的运算),而且大大的降低了耦合度,方便日后的维护及扩展
现在还有一个问题未解决,就是如何让程序根据用户输入的操作符实例化相应的对象呢?
解决办法:使用一个单独的类来实现实例化的过程,这个类就是工厂
代码如下:
<?php
/**
- 工程类,主要用来创建对象
- 功能:根据输入的运算符号,工厂就能实例化出合适的对象
-
/
class Factory{
public static function createObj($operate){
switch ($operate){
case '+':
return new OperationAdd();
break;
case '-':
return new OperationSub();
break;
case '':
return new OperationSub();
break;
case '/':
return new OperationDiv();
break;
}
}
}
$test=Factory::createObj('/');
$result=$test->getValue(23,0);
echo $result;
?>
再扩展一些
比如如何根据玩家输入的内容(尽管可以转化为其他字符串),来确定要制造的兵种,玩家不会输入代码:new Marine()。
和星际一样,PHP也没有终极兵种,如果类和接口是兵种的话,那么设计模式就是你的战术和控制,它可以让你靠各种兵种的搭配获胜。
待解决的问题:在人族的兵营,我们靠相应玩家的输入来动态确定要造的兵种,假设是机枪兵和火焰兵。
思路:动态的根据传递的数据,新建相应的类的对象。
简单工厂模式示例:
我们把机枪兵类的代码放入一个文件,Marine.php,它的代码如下:
<?php
class Marine {
//机枪兵攻击的方法
public function attack()
{
echo 'Marine attack';
}
}
?>
我们把火焰兵类的代码放入一个文件,Firebat.php,它的代码如下:
<?php
class Firebat {
//火焰兵攻击的方法
public function attack()
{
echo 'Firebat attack';
}
}
?>
主文件中的内容如下:
<?php
//兵种制造器的类
class BarracksCreator {
//制造兵种的方法
public create($createWhat)
{
//根据输入的参数,动态的把需要的类的定义文件载入
require_once($createWhat.'.php');
//根据输入的参数,动态的返回需要的类的对象
return new $createWhat;
}
}
//新建一个兵种制造器对象
$creator = new BarracksCreator();
//靠接收参数制造一个火焰兵对象
$troop1 = $creator->create('Marine');
$troop1->attack();
//靠接收参数制造一个机枪兵对象
$troop2 = $creator->create('Firebat');
$troop2->attack();
?>
用途总结:简单工厂模式可以将新建对象的任务进行封装,一旦需要增加新的返回类,只要修改负责新建对象的那部分代码。
实现总结:需要一个自动根据参数返回新建对象的工厂,比如上面兵种制造器BarracksCreator,使用的时候只需要将参数传递给他的生产方法create(),无需考虑具体的生产细节。www.szyoudun.com
简单工厂模式:
①抽象基类:类中定义抽象一些方法,用以在子类中实现
②继承自抽象基类的子类:实现基类中的抽象方法
③工厂类:用以实例化对象
使用工厂模式的目的或目标?
工厂模式的最大优点在于创建对象上面,就是把创建对象的过程封装起来,这样随时可以产生一个新的对象。
减少代码进行复制粘帖,耦合关系重,牵一发动其他部分代码。
通俗的说,以前创建一个对象要使用new,现在把这个过程封装起来了。
假设不使用工厂模式:那么很多地方调用类a,代码就会这样子创建一个实例:new a(),假设某天需要把a类的名称修改,意味着很多调用的代码都要修改。
工厂模式的优点就在创建对象上。
工厂模式的优点就在创建对象上。建立一个工厂(一个函数或一个类方法)来制造新的对象,它的任务就是把对象的创建过程都封装起来,
创建对象不是使用new的形式了。而是定义一个方法,用于创建对象实例。
每个类可能会需要连接数据库。那么就将连接数据库封装在一个类中。以后在其他类中通过类名:
为什么引入抽象的概念?
想一想,在现实生活中,当我们无法确定某个具体的东西的时候,往往把一类东西归于抽象类别。
工厂方法:
比如你的工厂叫做“香烟工厂”,那么可以有“七匹狼工厂”“中华工厂”等,但是,这个工厂只生厂一种商品:香烟;
抽象工厂:无法描述它到底生产什么产品,它生产很多类型的产品(所以抽象工厂就会生成子工厂)。
你的工厂是综合型的,是生产“一系列”产品,而不是“一个”,比如:生产“香烟”,还有“啤酒”等。然后它也可以有派生出来的具体的工厂,但这些工厂都是生产这一系列产品,只是可能因为地域不一样,为了适应当地人口味,味道也不太一样。
工厂模式:理解成只生成一种产品的工厂。比如生产香烟的。
工厂方法:工厂的一种产品生产线 。比如键盘的生成过程。
别人会反驳:吃饱了没事干,一定要修改类名称呢?这个说不定。一般都不会去修改类名称。
其实工厂模式有很多变体,抓住精髓才是关键:只要是可以根据不同的参数生成不同的类实例,那么就符合工厂模式的设计思想。
这样子让我联想到框架中经常会有负责生成具体类实例的方法供调用。
看完文章再回头来看下这张图,效果会比较好
采用封装方式
<?php
class Calc{
/**
- 计算结果
- @param int|float $num1
- @param int|float $num2
- @param string $operator
- @return int|float
/
public function calculate($num1,$num2,$operator){
try {
$result=0;
switch ($operator){
case '+':
$result= $num1+$num2;
break;
case '-':
$result= $num1-$num2;
break;
case '':
$result= $num1*$num2;
break;
case '/':
if ($num2==0) {
throw new Exception("除数不能为0");
}
$result= $num1/$num2;
break;
}
return $result;
}catch (Exception $e){
echo "您输入有误:".$e->getMessage();
}
}
}
$test=new Calc();
// echo $test->calculate(2,3,'+');//打印:5
echo $test->calculate(5,0,'/');//打印:您输入有误:除数不能为0
?>
优点:以上代码使用了面向对象的封装特性,只要有了include这个类,其他页面就可以随便使用了
缺点:无法灵活的扩展和维护
比如:想要增加一个“求余”运算,需要在switch语句块中添加一个分支语句,代码需要做如下改动
<?php
class Calc{
public function calculate($num1,$num2,$operator){
try {
$result=0;
switch ($operator){
//......省略......
case '%':
$result= $num1%$num2;
break;
//......省略......
}
}catch (Exception $e){
echo "您输入有误:".$e->getMessage();
}
}
}
?>
代码分析:用以上方法实现给计算器添加新的功能运算有以下几个缺点
①需要改动原有的代码块,可能会在为了“添加新功能”而改动原有代码的时候,不小心将原有的代码改错了
②如果要添加的功能很多,比如:‘乘方’,‘开方’,‘对数’,‘三角函数’,‘统计’,或者添加一些程序员专用的计算功能,比如:And, Or, Not, Xor,这样就需要在switch语句中添加N个分支语句。想象下,一个计算功能的函数如果有二三十个case分支语句,代码将超过一屏,不仅令代码的可读性大大降低,关键是,为了添加小功能,还得让其余不相关都参与解释,这令程序的执行效率大大降低
解决途径:采用OOP的继承和多态思想
<?php
/**
- 操作类
- 因为包含有抽象方法,所以类必须声明为抽象类
*/
abstract class Operation{
//抽象方法不能包含函数体
abstract public function getValue($num1,$num2);//强烈要求子类必须实现该功能函数
}
/** - 加法类
*/
class OperationAdd extends Operation {
public function getValue($num1,$num2){
return $num1+$num2;
}
}
/** - 减法类
*/
class OperationSub extends Operation {
public function getValue($num1,$num2){
return $num1-$num2;
}
}
/** - 乘法类
/
class OperationMul extends Operation {
public function getValue($num1,$num2){
return $num1$num2;
}
}
/** - 除法类
*/
class OperationDiv extends Operation {
public function getValue($num1,$num2){
try {
if ($num2==0){
throw new Exception("除数不能为0");
}else {
return $num1/$num2;
}
}catch (Exception $e){
echo "错误信息:".$e->getMessage();
}
}
}
?>
这里采用了面向对象的继承特性,首先声明一个虚拟基类,在基类中指定子类务必实现的方法(getValue())
分析:通过采用面向对象的继承特性,我们可以很容易就能对原有程序进行扩展,比如:‘乘方’,‘开方’,‘对数’,‘三角函数’,‘统计’等等。
<?php
/**
- 求余类(remainder)
-
*/
class OperationRem extends Operation {
public function getValue($num1,$num2){
return $num1%$num12;
}
}
?>
我们只需要另外写一个类(该类继承虚拟基类),在类中完成相应的功能(比如:求乘方的运算),而且大大的降低了耦合度,方便日后的维护及扩展
现在还有一个问题未解决,就是如何让程序根据用户输入的操作符实例化相应的对象呢?
解决办法:使用一个单独的类来实现实例化的过程,这个类就是工厂
代码如下:
<?php
/**
- 工程类,主要用来创建对象
- 功能:根据输入的运算符号,工厂就能实例化出合适的对象
-
/
class Factory{
public static function createObj($operate){
switch ($operate){
case '+':
return new OperationAdd();
break;
case '-':
return new OperationSub();
break;
case '':
return new OperationSub();
break;
case '/':
return new OperationDiv();
break;
}
}
}
$test=Factory::createObj('/');
$result=$test->getValue(23,0);
echo $result;
?>
再扩展一些
比如如何根据玩家输入的内容(尽管可以转化为其他字符串),来确定要制造的兵种,玩家不会输入代码:new Marine()。
和星际一样,PHP也没有终极兵种,如果类和接口是兵种的话,那么设计模式就是你的战术和控制,它可以让你靠各种兵种的搭配获胜。
待解决的问题:在人族的兵营,我们靠相应玩家的输入来动态确定要造的兵种,假设是机枪兵和火焰兵。
思路:动态的根据传递的数据,新建相应的类的对象。
简单工厂模式示例:
我们把机枪兵类的代码放入一个文件,Marine.php,它的代码如下:
<?php
class Marine {
//机枪兵攻击的方法
public function attack()
{
echo 'Marine attack';
}
}
?>
我们把火焰兵类的代码放入一个文件,Firebat.php,它的代码如下:
<?php
class Firebat {
//火焰兵攻击的方法
public function attack()
{
echo 'Firebat attack';
}
}
?>
主文件中的内容如下:
<?php
//兵种制造器的类
class BarracksCreator {
//制造兵种的方法
public create($createWhat)
{
//根据输入的参数,动态的把需要的类的定义文件载入
require_once($createWhat.'.php');
//根据输入的参数,动态的返回需要的类的对象
return new $createWhat;
}
}
//新建一个兵种制造器对象
$creator = new BarracksCreator();
//靠接收参数制造一个火焰兵对象
$troop1 = $creator->create('Marine');
$troop1->attack();
//靠接收参数制造一个机枪兵对象
$troop2 = $creator->create('Firebat');
$troop2->attack();
?>
用途总结:简单工厂模式可以将新建对象的任务进行封装,一旦需要增加新的返回类,只要修改负责新建对象的那部分代码。
实现总结:需要一个自动根据参数返回新建对象的工厂,比如上面兵种制造器BarracksCreator,使用的时候只需要将参数传递给他的生产方法create(),无需考虑具体的生产细节。www.szyoudun.com




