文章目录
  1. 1. 可视化编辑器
    1. 1.1. 为什么用Node?
    2. 1.2. 启动项目
    3. 1.3. 使用MongoDB(mongoose)存储
    4. 1.4. Angular的视图、控制器和指令。
    5. 1.5. iframe子页面用了jQuery来做DOM操作
    6. 1.6. 模块化的写法
    7. 1.7. 关于拖拽
    8. 1.8. 打包压缩怎么做?
    9. 1.9. 其他
  2. 2. 参与某移动web框架的开发
    1. 2.1. 滚动焦点图
    2. 2.2. 标题下拉框
    3. 2.3. 加载中动画
    4. 2.4. 柱形图
  3. 3. Java web系列项目小结

这是我对自己曾经做过的比较大的东西的大概梳理(小东西,以后我会自己注意整理,每做完就整理),写给自己的,记录的东西可能良莠不齐。如果路过的小伙伴有兴趣,也欢迎来指导。^ ^

以前做东西,基本不会总结写什么东西,而我记性又差(我总觉得自己像只只有七秒记忆的鱼,很多东西很快就忘记),所以导致之前做过的东西,只能记得个大概,细节什么的如果不看代码就完全忘了。
以前做东西的风格比较狂放,通常不会考虑太多,有很多时候因为时间紧张,当时很感兴趣的东西,也没有太多时间来深入学习,通常是完成功能就OK了,然后过了一阵子自己也就忘记要深入学习这样一个东西这件事了。

现在的我知道总结有多么重要,也明白以前会的那么多东西,现在记不得多少的痛苦。“出来混总是要还的”,作为补偿吧,我将把曾经做过的东西全部梳理一遍(这完全是个体力活,以后再不敢做完东西不总结了…),把我现在仍然觉得有价值的东西,需要记录的东西梳理一下。希望看到我以前的代码看到自己的不足,如果现在来做我有什么更好的解决方法吗?


可视化编辑器

目的:可拖拽的可视化编辑器,所见即所得,配置组件属性,存储当前版本,历史版本管理,压缩下载。
技术:Node.js(express)+AngularJS+MongoDB(mongoose)+Javascript模块化+HTML5本地存储
意义:第一次接触Node.js、AngularJS、MongoDB,边学边写,收获颇丰,从界面到代码,每个细节均要自己设计并实现,锻炼设计架构能力。虽然做的不是特别完美,过程中学到的这些东西是无价之宝。

为什么用Node?

Node的特点:异步I/O、事件与回调函数、单线程、跨平台。
Node的应用场景:I/O密集型、合理调度可擅长CPU密集型业务、与遗留系统和平共处、分布式应用
好吧,我坦白,其实是好奇js也能写后台这回事,而且写了比较久的Java也挺无聊的。

启动项目

app.js(Node.js启动文件)
目的:使用Node.js构造了一个服务器,引入了一些需要的包,配置路由等。
总结:现在就记得当时在加入一些东西时在顺序方面试了很久,有些东西是必须要在某些东西前的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
var express = require('express')
, user = require('./routes/routes_user') //user的controller
, version = require('./routes/routes_version') //version的controller
, path = require('path')
, ejs = require('ejs')
, http = require('http')
, fs = require('fs') //文件操作
,file_system = require('fs')
,archiver = require('archiver') //用于压缩
, SessionStore = require("session-mongoose")(express);
var store = new SessionStore({
url: "mongodb://localhost/session",
interval: 120000 // expiration check worker run interval in millisec (default: 60000)
});
var app = express();
app.set('port', process.env.PORT || 3000);
app.set('views', __dirname + '/views');
app.engine('.html', ejs.__express); //让ejs模板文件,使用扩展名为html的文件。
app.set('view engine', 'html'); //修改文件扩展名ejs为html
app.use(express.favicon());
app.use(express.logger('dev'));
app.use(express.bodyParser());
app.use(express.methodOverride());
app.use(express.cookieParser());
app.use(express.cookieSession({secret : 'ssm-ide'}));
app.use(express.session({
secret: "ssm-ide",
store: store,
cookie: { maxAge: 900000 } // expire session in 15 min or 900 seconds
}));
app.use(function(req, res, next){
res.locals.user = req.session.user;
next();
});
app.use(app.router);
app.use(express.static(path.join(__dirname, 'app')));
if (app.get('env') === 'development') {
app.use(express.errorHandler());
}
// angular启动页
app.get('/', function (req, res) {
res.sendfile('app/index.html');
});
app.post('/register', user.doUserAdd);
//以及很多类似的路由配置
app.post('/compress', function(req,res){
//压缩,稍后详写
});
http.createServer(app).listen(app.get('port'), function () {
console.log('Express server listening on port ' + app.get('port'));
});

使用MongoDB(mongoose)存储

(以注册为例,删减了与注册示例无关的代码,其他示例类似注册)
controller层目录结构:
项目总结
model层目录结构:
项目总结

routes_user.js:

1
2
3
4
5
6
7
8
9
10
11
12
var User = require('./../models/models_user.js');
exports.doUserAdd = function(req, res) {
var newUser=req.body;
User.save(newUser,function(err){
if(err) {
res.send({'success':false,'err':err});
} else {
exports.doUserVersionInit(req, res);
}
});
};

models_user.js:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
var mongodb = require('./mongodb');
var Schema = mongodb.mongoose.Schema;
var UserSchema = new Schema({
username:{ type:String, index: true } ,
password:String,
limit:{type:Number,default:3},
admin:{type:Boolean,default:false},
register_time : { type: Date, default: Date.now },
register_confirm : {type:Boolean,default:false}
});
var User = mongodb.mongoose.model("User", UserSchema);
var UserDAO = function(){};
UserDAO.prototype.save = function(obj, callback) {
var instance = new User(obj);
instance.save(function(err){
callback(err);
});
};
//省略很多其他方法代码...
module.exports = new UserDAO();

mongodb.js:

1
2
3
var mongoose = require('mongoose');
mongoose.connect('mongodb://localhost/ssm-ide');
exports.mongoose = mongoose;

Angular的视图、控制器和指令。

这里不想写太多,当时都是照着文档上面来的,可是不能完全从文档上获得想要的东西,有时候还是要参考很多资料,别人遇到的问题,还有要动一些脑筋才能解决。

其实用Angular时是很纠结和别扭的,因为和jQuery的思想完全不同,不过一旦了解了它,它给你带来的方便也是很大的,它让我的代码更整洁更规范,而且,不是一坨一坨的了。

在这个项目中的每个组件都需要重写相应的配置属性模板,Angular的$routeProvider在做这样的事让我觉得方便;
Angualr的双向绑定在这里也大有用处,因为动态设置组件的时候设置文字,是要及时获取页面上的更新,才能做到所见即所得的;
用Angular写组件也是意想不到的方便,其实必要时也可以使用jQuery插件的,就是麻烦点(好吧是很麻烦);

摘抄一些项目中的代码:

路由:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
var app = angular.module('app', ['ngResource','ngSanitize','ngRoute']);
app.config(['$routeProvider', '$locationProvider',
function($routeProvider, $locationProvider) {
$routeProvider
.when('/listul', {
controller:'listultplCtrl',
templateUrl:'views/tpl/listul.html'
})
.when('/page', {
controller:'pagetplCtrl',
templateUrl:'views/tpl/pages.html'
})
.when('/preview', {
templateUrl:'views/tpl/preview.html'
})
.when('/tabview', {
controller:'tabCtrl',
templateUrl:'views/tpl/tab.html'
}) //省略其他很多组件的路由
.otherwise({
redirectTo:'/page'
});
// configure html5 to get links working on jsfiddle
// $locationProvider.html5Mode(true);
}]);

Angular实现可折叠菜单组件(Angular能自己定义html标签 属性 等,很帅)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
app.directive('accordionmenu',function() {
return {
restrict:'EA',
replace:true,
transclude:true,
scope:{title:'=accordionmenuTitle',show:'=accordionmenuShow'},
template:'<div>'+
'<li class="accordionmenu_title" ng-click="toggle()">项目总结温故知新 - web前端类</li>' +
'<div ng-show="showMe" ng-transclude></div>' +
'</div>',
link : function($scope, $element,$attrs) {
$scope.showMe=$scope.show;
$scope.toggle=function toggle(){
$scope.showMe=!$scope.showMe;
}
}
}
});

在Angular里使用jQuery插件(话说还是用Angular自己写可能要好一些,在Angular里用jQuery插件有点纠结虽然也能用)

1
2
3
4
5
6
7
8
9
10
11
app.directive('ngShowfuncpane',function() {
return {
link : function($scope, $element) {
$($element).bind('click',function(){
var menuRight = $( '#cbp-spmenu-s2' )[0];
classie.toggle( this, 'active' );
classie.toggle( menuRight, 'cbp-spmenu-open' );
})
}
};
});

不同组件不同的模板
以tabview的template为例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
var app = angular.module('app');
app.controller('tabCtrl', ['$scope', '$location',function tabCtrl($scope, $location) {
if(typeof $scope.attr == 'undefined') {
$location.url('/');
return;
}
// 更改列表圆角操作
$scope.changeIsWrapDivCorner=function changeIsWrapDivCorner(){
var data = {
'seq':$scope.seq,
'directive':'changeIsWrapDivCorner',
'attr':{'isWrapedCorner':$scope.isWrapedCornerChecked}
}
sendMessage_funcdirective(data);
}
//省略一些方法......
$scope.$watch('attr', function() {
// 第n个列表项
$scope.tabItemNo=[];
for(var i=1;i<=$scope.attr.tabchildCount;i++){
$scope.tabItemNo.push(i);
}
// 列表圆角
$scope.isWrapedCornerChecked=$scope.attr.isWrapedCorner;
// 当前列表项文字内容
$scope.tabItemContentInput=$scope.attr.tabItemContent;
// 当前选的正操作的第N个列表项
$scope.tabItemNoSelected=$scope.attr.tabItemNo;
},true);
$scope.TabTitle = '标签布局设置';
}]);

其中sendMessage_funcdirective(data);是发送指令函数,用了一个包,其封装了web worker,我用它来实现iframe父子页面通信
$watch是用来监听,只要属性值发生变化就立即触发。

iframe子页面用了jQuery来做DOM操作

iframe子页面不同于父页面,我使用了jQuery,因为根据父页面的指令来动态操控子页面的组件,实现属性的变更,用DOM直接操作更为方便。
虽然有人质疑父页面用Angular子页面用jQuery这种方式很奇怪,但我觉得这是最适合的方式,对的东西做对的事,Angular不适合做DOM操作就让他做他适合做的事,我一点不觉得奇怪。

子页面的实现细节就不说了。大概就是接收父页面传来的指令,使用jQuery让其做相应的操作。
为了可重用我把子页面属性的更改,写成了jQuery插件的样子,类似这样子,不过每个组件的操作有所不同,实现可重用也是有点困难,索性就每个组件都有这么个jQuery插件文件,有新操作就可以通过扩展相应方法来做。

此为列表项jQuery插件文件方法的缩略版:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
(function ($) {
/****************************************/
/* 各种属性get函数
/****************************************/
// 获取子项个数
$.fn.getChildCount = function () {
var childCount=$(this).children().length;
return childCount;
}
// 获取当前元素是否为圆角
$.fn.getIsRoundedCorner = function () {
var _this=$(this);
return _this.hasClass("ROUNDED")?'yes':'no';
}
// 获取当前列表项是否为链接
$.fn.getListItemLink = function () {
var _this=$(this);
return _this.find("a").length!=0?'yes':'no';
}
$.fn.getListItemLinkHrefContent = function () {
var _this=$(this);
var hrefContent='';
aLabelHrefList.aLabelEffect();
hrefContent=_this.find("a").attr("href");
hrefContent=hrefContent.replace(/#/,"");
aLabelHrefList.aLabelDisable();
return hrefContent;
}
//省略很多方法...
/****************************************/
/* 各种操作函数
/****************************************/
// 列表项个数+1
$.fn.addli = function (options) {
var _this=$(this);
_this.append(_this.children(':last')[0].outerHTML);
sendMessage_funcpanelinit(_this);
}
// 改变圆角属性
$.fn.changeIsRoundedCorner = function (isRoundedCornerChecked) {
var _this=$(this);
_this.toggleCusClass(isRoundedCornerChecked,"ROUNDED");
sendMessage_funcpanelinit(_this);
}
// 改变选的是第几项列表项时
$.fn.changeListItemNo = function (listItemNo) {
var _this=$(this);
var args={
listItemNo:listItemNo
}
sendMessage_funcpanelinit(_this,_this.getInitAttr(args));
}
// 改变列表项内容是否带链接
$.fn.changeListItemLink = function (listItemNo,listItemLink) {
var _this=$(this).children().eq(listItemNo-1);
var content='',
small='';
if(listItemLink=='yes'){
if(_this.find('a').length==0){
content =_this.text();
if(_this.find("small").length!=0){
var tmp= eval("/" + _this.find("small").text() + "/ig");
content = content.replace(tmp, '');
small = _this.children("small").parent().html().replace(content, '');
}
content="<a href='#'>"+content+"</a>"+small;
_this.html(content);
}
}else{
content=_this.find("a").text();
_this.prepend(content).find("a").remove();
}
}
//省略很多方法...
})(jQuery);

模块化的写法

为了实现像java那个样子,封装起某个类,能调用类的某个方法,还需要暂存起某个变量的值,这个真的没少下工夫,当时的我不知道Javascript有模块化的写法,也不知道闭包(乃们不要鄙视我这个当时没看过Javascript书的孩纸啦),按照自己的想法去查资料最终终于了解到闭包可以使变量值保存在内存中这样的事。

应用模块化的写法,我封装了很多个模块,解决了诸如在线可视化编辑js代码(在线编辑,删除,随当前版本被存入数据库),预览(预览情况下才链接才可以跳转,编辑状态链接不可跳转)等等困扰我很久的疑难杂症。

摘录一些可视化编辑js的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
var eventDataHandle = (function(){
// 将新增的事件绑定加入eventlist
var insertEventjsIntoEventlist = function(msg){
var _eventlist=eventList.getEventlist();
var addedNewFuncIntoFunclist=false;
for(var i = 0;i<_eventlist.length;i++){
var thislist=_eventlist[i];
if(thislist.seq==msg.seq){
var childattrAreSame=false,
CHILDATTRARESAMEFLAG=false,
funcindex=0;
for(var j=0;j<thislist.funclist.length;j++){
var thisfunc=thislist.funclist[j];
// 对于新增的这个事件和原来funclist里的事件们,判断他们的functype是否相同
// 如果相同则判断是否操作在funclist里面同一个子元素,
// 如果相同就替换funcContent
// 如果不相同就添加新事件进funclist
// 如果functype找不到相同的就直接添加新事件进funclist
if(thisfunc.funcType.val==msg.funcType.val){
for(var key in thisfunc.childattr){
var tmp1=msg.childattr[key],
tmp2=thisfunc.childattr[key];
if(tmp1==tmp2){
childattrAreSame=true;
}else{
childattrAreSame=false;
break;
}
}
if(childattrAreSame){
CHILDATTRARESAMEFLAG=true;
funcindex = j;
break;
}
}
}
if(CHILDATTRARESAMEFLAG){
_eventlist[i].funclist[funcindex].funcContent=msg.funcContent;
addedNewFuncIntoFunclist=true;
break;
}
if(!addedNewFuncIntoFunclist){
var tmpfunc={
eventid:msg.eventid,
childattr:msg.childattr,
funcType:msg.funcType,
funcContent:msg.funcContent
}
_eventlist[i].funclist.push(tmpfunc);
addedNewFuncIntoFunclist=true;
break;
}
}
}
if(!addedNewFuncIntoFunclist){
var tmpevent={
seq:msg.seq,
funclist:[{
eventid:msg.eventid,
childattr:msg.childattr,
funcType:msg.funcType,
funcContent:msg.funcContent
}]
}
_eventlist.push(tmpevent);
}
eventList.setEventlist(_eventlist);
};
//省略一大堆方法...
var getWholeDefaultjslist=function(){
var _defaulteventlist=defaultjslist?defaultjslist.getDefaultjslist():[];
return _defaulteventlist;
}
return {
insertEventjsIntoEventlist: insertEventjsIntoEventlist,
getWholeDefaultjslist:getWholeDefaultjslist
//省略一大堆代码
}
})();

关于拖拽

使用了HTML5的拖拽写法
同样摘录部分代码:
HTML:

1
2
3
<span id="xx" draggable="true" ondragstart="drag(event)">
.................
</span>

JS:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
if (document.readyState=="complete")
{
dragEvent();
}
else
{
document.onreadystatechange = function()
{
if (document.readyState == "complete")
{
dragEvent();
}
}
}
function dragEvent(){
function onDragOver(event){
if(event.preventDefault) event.preventDefault();
if (event.stopPropagation) event.stopPropagation();
else event.cancelBubble = true;
return false;
}
function onDrop(event){
if(event.preventDefault) event.preventDefault();
if (event.stopPropagation) event.stopPropagation();
else event.cancelBubble = true;
var id = event.dataTransfer.getData("Text");
var item = document.getElementById(id);
addtores(item, id);
return false;
}
function addtores(item, id) {
var item =$('.'+id+'obj')[0],
itemid=$('.'+id+'id').html(),
itemjs=$('.'+id+'js').html(),
clone = item.cloneNode(true);
if(itemid!=undefined){
$(clone).removeClass(id+'obj').attr({id:itemid,type:id,seq:Math.uuid()});
}else{
$(clone).removeClass(id+'obj').attr({type:id,seq:Math.uuid()});
}
// 将拖拽的东西添加到iframe中当前的页面
iframeinfo.getSpeIframeBlockobj(iframeinfo.getPageid()).appendChild(clone);
// --- 触发sortable and funclistener BEGIN ---
var objdata={'directive':'main'};
objdata = JSON.stringify(objdata);
messenger.targets['mainIframe'].send(objdata);
// --- 触发sortable and funclistener END ---
if(itemjs!=undefined){
var jsdata={
'directive':'defaultjsbind',
'objtype':id,
'objid':itemid?itemid:'',
'defaultjs':itemjs
};
jsdata = JSON.stringify(jsdata);
console.log("jsdata",jsdata);
messenger.targets['mainIframe'].send(jsdata);
}
}
}
function drag(ev)
{
if(iframeinfo.getPagestate()!="preview"){
ev.dataTransfer.setData("Text",ev.target.id);
}else{
alert("预览状态不可添加组件!~");
}
}

iframe子页面的拖拽排序是用的jQueryUI…

打包压缩怎么做?

贴一个打包压缩代码,是最开始贴的app.js里省略的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
app.post('/compress', function(req,res){
var newbodycode=req.body.bodycode,
bindfunc=req.body.bindfunc,
defaultjs=req.body.defaultjs,
bindfuncreg=/\r/g,
output = file_system.createWriteStream('app/res.zip'),
archive = archiver('zip'),
scriptreg=/<script\s\S*/g;
bindfunc="var ssM = $.ssm();$(function (){"+defaultjs+bindfunc.replace(bindfuncreg, '')+"});";
newbodycode="<body>"+newbodycode.replace(scriptreg, '')+'<script src="index.js" type="text/javascript" charset="utf-8"></script>'+"</body>";
fs.readFile('app/ires/res.html', 'utf8', function(err, data) {
if(err) {
return console.log(err);
}
var linkreg=/<link\shref=.*/,
bodyreg=/<body>[\S\s]*?<\/body>/,
tmphtmlcontent=data.replace(linkreg, ''),
htmlcontent=tmphtmlcontent.replace(bodyreg,newbodycode);
fs.writeFile('app/ires/index.html', htmlcontent, function(err) {
if(err) {
return console.log(err);
}
console.log('writing done to index.html');
})
});
fs.writeFile('app/ires/index.js', bindfunc, function(err) {
if(err) {
return console.log(err);
}
console.log('writing done to index.js');
})
output.on('close', function () {
console.log(archive.pointer() + ' total bytes');
res.write("success");
console.log('archiver has been finalized and the output file descriptor has closed.');
res.end();
});
archive.on('error', function(err){
throw err;
});
archive.pipe(output);
archive.bulk([
{ expand: true, cwd: 'app/ires', src: ['*.js','*.css','index.html','*/**']}
]);
archive.finalize();
});

代码略复杂我写的时候也是很纠结的呀,因为打包压缩的结果是用来给用户下载的,所以想要使生成的文件尽量整洁,去掉由编辑器生成的一些东西,所以写了一些正则去掉一些结果文件不需要的文件之类的,然后再将这些字符写进新的文件(很多个新文件)并打包以供下载。

弄到现在我自己也快晕死了,一次看这么多代码,吃不消…

其他

大概的差不多了,贴上来的都是很简单的,复杂的处理,和一些奇葩的问题就不贴了。
剩下比较有意义的可能就是设计方面的东西,比如父子页面相互通信时候需要添加一些必要的信息比如一个独一无二的码,不然真的没有办法改变相应的属性的。
图标方面用到了图标字体,用AI来画,用阿里妈妈图标库那个来生成,搞定。

当时做这个东西,很纠结,因为太多东西要考虑,搞不好就要放弃掉之前的想法重头来过,由于设计不成熟就开始写代码写出的问题也是成堆成堆的。
所以我明白了啊..设计是如此重要,据说有很多程序员都是这样才加班的,其实如果设计好了,写代码的时间会大大缩短的吧,当时我也不懂这个道理,有点想法,就扑上去写代码,结果最后写出很多问题。
这个项目学到了很多,也思考很多,不仅是作为程序员的角度。


参与某移动web框架的开发

其实说是“参与开发”,可是当我参与的时候,整个项目其实已经只是我自己一个了。
当时我虽然自己作为练习写过一个js版俄罗斯方块,可是没有经常用js没有很熟悉,css也不熟悉,一切从头开始。
那段时间其实是我不愿提及的一段时间,什么都不会的我,面对着那么多文件,真的快晕掉了,而且已经存在的也并不完美
庆幸的是内核的东西我不用动,必要的时候读一下懂了就可以,我要做的是为其添加插件,按照已有的接口,添加插件就可以了,没有比这更让我开心的了。

可是也不是一帆风顺,由于用的是早期的zepto(我曾尝试过换成新的版本可是问题更多),还是碰到一些奇葩的问题,只能默默的研究解决,实在不行就只能另辟蹊径,不过那段时间真的是挺锻炼人的,做了几个插件之后我觉得我的Javascript已经比较熟悉了(虽然现在看来写的代码还是一坨屎)。

做什么插件完全由我自己决定,前前后后做了:滚动焦点图,标题下拉框,加载中动画,柱形图
让我看看以前写的东西有什么需要注意的东西吧~

滚动焦点图

思路:获取组件中列表子元素,根据自定义属性(如不设置有默认属性),设置高度,将每个列表子元素中的图片的宽设置成屏幕宽度(由于是做手机上的插件,当时就那样写了),整个列表宽度设置成屏幕宽度*图个数,设置定时器,根据设定的时间间隔属性,通过animate动画设置列表的left值,显示前一张后一张,同时更改右下角指示第几张图的小按钮颜色。

:做的第一个组件就有了,属性设置的概念(通过data-setting设置属性),并给出默认属性值
:现在看来这个组件问题很多,作为一个组件,应该每个都是一个独立的实例才好,而这里,统一获取的带某class的div,这样所有的组件并不能达到独立的目的,如果用经典的继承、构造函数就可以做到。最后一个图片和第一个图片的相互切换的处理很粗糙,如果做成无限循环更好。每个图片的宽度设置为全屏幕的宽度的处理欠妥。

有空一定要再写一个表现完美的滚动焦点图。

标题下拉框

思路:标题所在的div加CLASS=”HEADSELECT” 和 id=”XX”(用于绑定回调函数)所在div添加ul列表里面是下拉列表框的列表内容。
点击标题时,切换标记(展开/合起),并添加或删除相应的class,并触发step函数(动画效果,慢慢展开和慢慢合起的动画,不是突然出现突然消失)及切换遮罩层的显示和不显示

step函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
var step = function() {
height = display? (height + 5): (height - 5);
ul_height = display? ((height+21)*$count): 0;
if (height < 0) {
height = 0;
} else if (height > 20) {
height = 20;
}
$option_li.height(height + "px");
$option_ul.height( ul_height+ "px");
if (height > 0 && height < 20) {
timer = setTimeout(step, 40);
}
};

监听器(elem:包含标题和下拉框ul的div的id,callback:回调函数):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
addListener: function(elem, callback){
var $elem = $(elem),
$list = $elem.children('ul');
$list.on('click', function(event) {
event.preventDefault();
var target = event.target,
$target = $(target);
if ($target.is('li')||$target.parent().is('li'))
{
callback.call(elem, target);
}
});
}

:思路较清晰
:异常处理不太完备

加载中动画

思路:觉得这个插件主要功夫在于CSS3的写法,剩下就是触发时把相关html加上….传参时要传show/hide及相关配置属性参数

CSS代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
#ssm .LOADING{
position: absolute;
top:50%;
left:50%;
margin-left:-25px;
margin-top:-25px;
width: 50px;
height: 50px;
z-index: 100;
}
@-webkit-keyframes trans{
from{
opacity:1;
}
to{
opacity:0.1;
}
}
@-moz-keyframes trans{
from{
opacity:1;
}
to{
opacity:0.1;
}
}
#ssm .LOADING div{
position: relative;
width: 100%;
height: 100%;
}
#ssm .LOADING div span{
display: block;
background: #000000;
-webkit-border-radius: 100%;
-moz-border-radius: 100%;
position: absolute;
-webkit-animation: trans 0.8s ease-in infinite alternate;
}
.LOADING div span:nth-child(1){
width: 18%;
height: 18%;
left: 50%;
top: 0%;
margin-left: -9%;
}
#ssm .LOADING div span:nth-child(2){
width: 16.5%;
height: 16.5%;
top: 15%;
left: 28%;
margin-left: -8.25%;
margin-top: -8.25%;
-webkit-animation-delay:0.1s;
}
#ssm .LOADING div span:nth-child(3){
width: 15%;
height: 15%;
top: 30%;
left: 12%;
margin-left: -7.5%;
margin-top: -7.5%;
-webkit-animation-delay:0.2s;
}
#ssm .LOADING div span:nth-child(4){
width: 13.5%;
height: 13.5%;
left: 0%;
top: 50%;
margin-top: -6.75%;
-webkit-animation-delay:0.3s;
}
#ssm .LOADING div span:nth-child(5){
width: 12%;
height: 12%;
bottom: 28%;
left: 12%;
margin-left: -6%;
margin-bottom: -6%;
-webkit-animation-delay:0.4s;
}
#ssm .LOADING div span:nth-child(6){
width: 10.5%;
height: 10.5%;
bottom: 12%;
left: 28%;
margin-left: -5.25%;
margin-bottom: -5.25%;
-webkit-animation-delay:0.5s;
}
#ssm .LOADING div span:nth-child(7){
width: 9%;
height: 9%;
left: 50%;
bottom: 0%;
margin-left: -4.5%;
-webkit-animation-delay:0.6s;
}
#ssm .LOADING div span:nth-child(8){
width: 7.5%;
height: 7.5%;
right: 25%;
bottom: 10%;
margin-right: -3.75%;
margin-bottom: -3.75%;
-webkit-animation-delay:0.7s;
}
#ssm .LOADING div span:nth-child(9){
width: 6%;
height: 6%;
bottom: 26%;
right: 8%;
margin-right: -3%;
margin-bottom: -3%;
-webkit-animation-delay:0.725s;
}
#ssm .LOADING div span:nth-child(10){
width: 4.5%;
height: 4.5%;
top: 50%;
right: 0%;
margin-left: -2.25%;
-webkit-animation-delay:0.75s;
}
#ssm .LOADING div span:nth-child(11){
width: 3%;
height: 3%;
top: 30%;
right: 9%;
margin-right: -1.5%;
margin-top: -1.5%;
-webkit-animation-delay:0.775s;
}
#ssm .LOADING div span:nth-child(12){
width: 1.5%;
height: 1.5%;
top: 14%;
right: 26%;
margin-right: -0.75%;
margin-top: -0.75%;
-webkit-animation-delay:0.78s;
}

:这CSS代码写下来也是蛮拼的
:CSS3没有太深入研究过,不知还有更简练的写法没有,不过貌似我只写了webkit内核的啊 0.0 晕~

柱形图

思路:这个组件虽然完成了很长时间了但是我印象深刻啊…因为想要做到让给的数据生成的柱形图比例还有刻度那些比较协调还是花了些功夫写算法啊….而且是第一次接触canvas,重点就是用canvas按照计算的比例把图画出来。
做之前调研过图表插件之类的,人家都是写好的重用的东西之类的啊,很多插件都是用了以前写过的方法之类的…我当时没有那么高的眼界,而且我就只做个柱形图,以前又没有做过,这个做不做得出来都不知道,只好不管那些,能写出来个能用的就行了,如果之后要做其他图表,再来将这个整理一下(那一定是师弟师妹们要做的了)。

算法的思路(写在注释里面的一些东西,现在看还是有点难理解,当时写也是蛮纠结的):
获取每个柱子 经过比例换算后的 高度对应的px
获取图区域y轴总px
y_show_max(y轴显示数据的最大值)/px_max(y轴总px)=data(数据大小)/data_px(数据对应高度的px)
获取输入数据最大值对应的能被5整除的划分的阶 比如 0.6即0.05 1.032即0.5 12即5 120即50
获取数据最大值小一级的5的倍数 如100位即50 10位为5…
获取y轴显示数据的最大值
输入数据最大值/阶 取上整 再乘回阶 即得到比输入数据最大值大一些且为5的倍数的y轴显示最大值
如果最小值为负数 则输入数据最小值/阶 取下整 再乘回阶 即得到比输入数据最小值小一些且为5的倍数的y轴显示最小值

有没有晕,反正我是有点晕了,大概的思路就是按照比例来啦,不过就是要分析一下这些要生成柱形图的数据,最大值最小值是什么级别的,来划分y轴的跨度之类的,而且要让最大值显示在整个柱形图的高度的80%处。

贴个最简单的坐标系的画法,其他比这个复杂,不过也差不多,画线或填充。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
// 创建一个画柱形图的 canvas画布
var canvas_graph = document.createElement('canvas');
// 设置刚刚创建的画柱形图的 canvas画布的属性
canvas_graph.id = target + "_1";
canvas_graph.width = parameter.c_width;
canvas_graph.height = parameter.c_height;
canvas_graph.style.position = "absolute";
canvas_graph.style.left = 16 + 5 + (data_level.toString().length + 1) * 6;//一个数字占6px 再设置其离数字5px的距离 16px是给yaxis_label留的
if (settings.yaixs_label == "") {
canvas_graph.style.left = 5 + (data_level.toString().length + 1) * 6;
}
canvas_graph.style.top = 30;
// 将柱形图的 canvas添加到容器div中
div_target.appendChild(canvas_graph);
// 获取一个用于在柱形图画布上绘图的环境
var canvas_graph_ctx = canvas_graph.getContext('2d');
// 画坐标
canvas_graph_ctx.beginPath();
// 坐标颜色
canvas_graph_ctx.strokeStyle = '#A0A0A0';
// 画笔轨迹是个L型
canvas_graph_ctx.moveTo(parameter.x_start, parameter.c_height - parameter.y_start);
canvas_graph_ctx.lineTo(parameter.x_start, parameter.y_start);
canvas_graph_ctx.lineTo(parameter.c_width + parameter.x_start, parameter.y_start);
canvas_graph_ctx.stroke();

优:给自己的执着点个赞,为了让显示更细腻,做了很多算法的处理,也是蛮拼的,
缺:虽然写了那么多注释,算法还是蛮难懂的,自己都看不太懂,维护是个问题,可重用方面也许提升,不过已经还不错啦。

很想深入学习一下canvas呢……


Java web系列项目小结

不可否认,我关于项目的启蒙全部来源于java项目,不过这个系列其实没什么好写的,关于前端方面主要就是jQuery的表单操作还有jQuery插件的使用、jstl的使用,做java的项目界面也主要用的bootstrap,主要的练习在于java后台的练习,所以这个系列我也就偷个懒啦,不那么仔细的搞咯~ 话说很久没写JAVA还真是有点陌生了呢..

jstl使用(摘录):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
........................................................
<form class="form-inline" role="form" method="post" action="<c:url value="/bill/billFilter.action"/>">
........................
</form>
<form name="frm" method="post">
<c:if test="${billList!=null&&billList.size()>0}">
<table width="100%" class="table table-condensed table-striped dataTable table-bordered table-hover">
<thead>
<tr style="background: #d7e6ea">
<th scope="col" width="10%" >序号</th>
<th scope="col" width="15%">账务类型</th>
<th scope="col" width="15%">资金数额</th>
<th scope="col" width="15%">日期</th>
<th scope="col" width="20%">备注</th>
<th scope="col" width="10%">删除</th>
<th scope="col" width="10%">修改</th>
</tr>
</thead>
<tbody>
<c:forEach items="${billList}" var="li" varStatus="status">
<tr>
<td>${status.index+1}</td>
<td>${li.billStyle}</td>
<td>${li.billMoney}</td>
<td>${li.billCraetedTime}</td>
<td>${li.billMemo}</td>
<td><a id="del" class="btn btn-default btn-xs " role="button" href="<c:url value="/bill/billDel.action?billId=${li.billId}"/>" onclick="return delBill()">删  除</a></td> </tr>
</c:forEach>
</tbody>
</table>
</c:if>
<c:if test="${billList==null||billList.size()==0}">
<div style="position: relative;top:100px;text-align: center">
<p><a style="font-size:50px;text-decoration: none;text-shadow: 0 3px 3px #FFF;color: #707781; font-size: 32pt; font-weight: bold; line-height: 25px; margin-bottom: 0;">你目前还没有账单信息哦   O__O"…</a></p>
<p><a style="font-size:25px;text-shadow: 0 3px 3px #FFF;color: #64818E; font-size: 15pt; font-weight: normal;" href="<%=path%>/jsp/add.jsp">现在去添加</a></p>
</div>
</c:if>
</form>

ajax和图表(jQuery图表插件-jQplot)生成(摘录):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
function ajaxChartMemo(){
$.ajax({
url:'/pf/bill/billStatisByMemo.action',
dataType:'json',
error:function(){
alert("error");
},
success:function(data){
chartData = [];
console.log(data);
$.each(data,function(index){
item=[];
item.push(data[index].billMemo);
item.push(data[index].cnt);
chartData.push(item);
});
createChartMemo(chartData);
}
});
}
function createChartMemo(data){
var plot1 = jQuery.jqplot ('chart3', [data],
{
title:'根据用途的账单百分比统计图',
seriesDefaults: {
// Make this a pie chart.
renderer: jQuery.jqplot.PieRenderer,
rendererOptions: {
// Put data labels on the pie slices.
// By default, labels show the percentage of the slice.
showDataLabels: true
}
},
legend: { show:true, location: 'e' }
}
);
}

还有很多好用的插件啦其实,像datepicker、验证表单及时提示错误消息那个插件(想不起名字,乃们不要怪我T_T)、uploadify(用于上传文件、图片那些)等等
这些都是做项目的神器呐,好了偷了个懒,就写到这吧,好困~

文章目录
  1. 1. 可视化编辑器
    1. 1.1. 为什么用Node?
    2. 1.2. 启动项目
    3. 1.3. 使用MongoDB(mongoose)存储
    4. 1.4. Angular的视图、控制器和指令。
    5. 1.5. iframe子页面用了jQuery来做DOM操作
    6. 1.6. 模块化的写法
    7. 1.7. 关于拖拽
    8. 1.8. 打包压缩怎么做?
    9. 1.9. 其他
  2. 2. 参与某移动web框架的开发
    1. 2.1. 滚动焦点图
    2. 2.2. 标题下拉框
    3. 2.3. 加载中动画
    4. 2.4. 柱形图
  3. 3. Java web系列项目小结