测试效果如图:


当鼠标划过的时候,需要显示更多的信息,因为GridPanel表格显示区域不够,所以把部分信息移动到 Tip 中显示。代码如下:

store: {
  type: 'EduCartoonStore',
  autoLoad: false,
  listeners: {
      'load': function() {
          // 得到当前的Gird
          var grid = Ext.getCmp('eduCartoonListGrid');
          var view = grid.getView();
          // 创建一个 Tip
          var tip = Ext.create('Ext.tip.ToolTip', {
              target: view.el,
              delegate: view.itemSelector,
              trackMouse: true,
              renderTo: Ext.getBody(),
              listeners: {
                  // 显示Tip
                  beforeshow: function updateTipBody(tip) {
                      var record = view.getRecord(tip.triggerElement);
                      var state = '';
                      var createTime = '';
                      var packageTime = '-';
                      var showHtml = '';
                      // 状态
                      if (record.data.status === 0) {
                        state = "<span style='color:red'>待审核</span>";
                      } else if (record.data.status === 1) {
                        state = "<span style='color:green'>已审核</span>";
                      } else if (record.data.status === -1) {
                        state = "<span style='color:grey'>已关闭</span>";
                      }
                      // 创建时间
                      createTime = formatDate(record.data.ctime);
                      // 打包时间
                      if (record.data.zipGenerateTime !== undefined) {
                        packageTime = formatDate(record.data.zipGenerateTime)
                      }
                      showHtml += '<p><b>ID编号</b>:' + record.data.id + '</p>';
                      showHtml += '<p><b>状态</b>:' + state + '</p>';
                      showHtml += '<p><b>创建时间</b>:' + createTime + '</p>';
                      showHtml += '<p><b>打包时间</b>:' + packageTime + '</p>';
                      tip.update(showHtml);
                  }
              }
          });
      }
  }
}

场景是这样的,用户编辑完文本之后,上传了一张图片。而这个时候并没有让编辑器重新获得焦点。就会导致提交的图片为:themes/default/images/spacer.gif 。原因是上传完成图片之后,数据正确返回但是并没有动态绑定到图片的 src 属性中。这个时候需要手动设置一次富文本编辑框的值,触发 change 事件。修改代码如下:

ueditor.all.js  24518行

function callback(){
try{
	var link, json, loader,
	body = (iframe.contentDocument || iframe.contentWindow.document).body,
	result = body.innerText || body.textContent || '';
	json = (new Function("return " + result))();
	link = me.options.imageUrlPrefix + json.url;
	if(json.state == 'SUCCESS' && json.url) {
		loader = me.document.getElementById(loadingId);
		loader.setAttribute('src', link);
		loader.setAttribute('_src', link);
		loader.setAttribute('title', json.title || '');
		loader.setAttribute('alt', json.original || '');
		loader.removeAttribute('id');
		domUtils.removeClasses(loader, 'loadingclass');
		me.execCommand('inserthtml', '');   // 就是这一行,记住了!!!
	} else {
		showErrorLoader && showErrorLoader(json.state);
	}
}catch(er){
	showErrorLoader && showErrorLoader(me.getLang('simpleupload.loadError'));
}
form.reset();
domUtils.un(iframe, 'load', callback);
}

修改源码后就可以解决这个问题啦。

因业务需要,只能动手来研究一个拖拽模块。研究之前觉得很简单,在我印象里好想类似的 Vue,jQuery 的插件非常多。应该是分分钟就可以搞定。结果花了很长的时间来研究这个玩意。现在把思路记下来方便下次查阅。

  • 创建拖拽对象
  • 确定“接受拖拽对象的”区域
  • 新建一个类,用于实现 ExtJS 中关于 “拖拽” 方法的实现
  • 使用新编写带有实现的类应用到拖拽对象中
  • 编写CSS来提升用户体验
  • 拖拽结束之后的节点复制和移动

基本上就是上述的几个步骤,为了方便查阅我把所有的脚本都构建在一个 JS 文件中,若真用到项目中,请严格按照 Sencha ExtJS 的 MVC 标准做代码的分离。代码如下:

/*
 * author:      liucan
 * email:       [email protected]
 * description: 系统顶部模块
 */

// 在这里需要重写拖拽的方法,基础的 API 并没有基于业务的实现。
// 拖拽完成,必须要判定是否拖放在指定的区域中,并且长宽要一致
// 对其当前的容器元素,TOP,LEFT 必须为 0
var overrides = {
    startDrag: function () {
        this.originalXY = this.el.getXY();
    },
    onDrag: function () {
        this.el.addCls('item-draging');
    },
    onDragEnter: function (e, id) {
        // console.log('onDragEnter');
        if(id != this.el.dom.parentNode.id){
            this.el.addCls('item-drag-ok');
        }else{
            this.onDragOut();
        }
    },
    onDragOver: function (e, id) {
    },
    onDragOut: function (e, id) {
        this.el.removeCls('item-drag-ok');
    },
    onDragDrop: function (e, id) {
    },
    onInvalidDrop: function () {
        // 无效拖动,需要使用动画还原。
        this.invalidDrop = true;
        this.el.removeCls('item-draging');    
    },
    endDrag: function (e, id) {
        this.el.removeCls('item-draging');
        if(this.invalidDrop === true){
            // 无效的移动
            var animCfgObj = {
                easing: 'elasticOut',
                duration: 1,
                scope: this,
                callback: function(){
                    this.el.dom.style.postion = '';
                }
            }
            this.el.setXY(this.originalXY);
            delete this.invalidDrop;
        }else{
            // 判定 TARGET 的值,并且做好磁力贴效果
            // 复制节点到指定元素,删除原有节点
            var newNode = this.el;
            if(e.item){
                e.item.firstChild.append(newNode.dom)
                newNode.dom.style = "";
            }
        }
    }
};

var pinyinTpl = new Ext.XTemplate(
    '<div id="pinyin_panel" class="pinyin-panel">',
        '<tpl for=".">',
            '<div class="pinyin-item">{.}<div class="reset-this">x</div></div>',
        '</tpl>',
    '</div>'
);
var fontTpl = new Ext.XTemplate(
    '<div class="font-panel">',
        '<tpl for=".">',
            '<div class="thumb-wrap">',
                '<div class="thumb-pinyin"></div>',
                '<div class="thumb-font">{.}</div>',
            '</div>',
        '</tpl>',
        '<div style="clear: both"></div>',
    '</div>'
);

Ext.define('xjcms.view.util.Pinyin', {
    extend: 'Ext.window.Window',
    alias: 'widget.Pinyin',
    title: '生成拼音',
    requires: [
        'xjcms.view.util.pinyin.PinyinController',
        'xjcms.view.util.pinyin.PinyinModel'
    ],
    controller: {
        type: 'pinyin'
    },
    viewModel: 'pinyin',
    width: 600,
    minHeight: 320,
    layout: 'fit',
    scrollable: false,
    // 遮罩层
    modal: true,
    constrain: true,
    closable: true,
    autoDestroy: true,
    draggable: true,
    resizable: false,
    items: [{
        xtype: 'form',
        bodyPadding: '10 10 10 10',
        items: [{
            xtype: 'container',
            layout: 'hbox',
            items: [{
                xtype: 'textfield',
                fieldLabel: '文字',
                labelWidth: 60,
                width: 500,
                emptyText: '请输入文字',
                regex: /^([\u4e00-\u9fa5]+)/,
                regexText: '该输入框仅仅只支持输入汉字',
                value: ''
            }, {
                xtype: 'button',
                text: '生成拼音',
                handler: function () {
                    // word-lib/convert2Pinyin
                    var text = this.prev().getValue();
                    var vm = this.up('window').getViewModel();
                    var vmStores = vm.storeInfo;
                    var fontData = [];
                    text = Ext.util.Format.trim(text);
                    // 请求数据
                    Ext.Ajax.request({
                        url: 'https://easy-mock.com/mock/5ae02d829c4a407f53115d81/rms/py',
                        method: 'GET',
                        params: {
                            font: text
                        },
                        success: function (response, opts) {
                            var obj = Ext.decode(response.responseText);
                            // 中文字和拼音字母
                            var font = obj.data.font;
                            var py = obj.data.py;
                            vmStores['fontStore'] = Ext.create('Ext.data.Store', {
                                data: font
                            });
                            vmStores['pinyinStore'] = Ext.create('Ext.data.Store', {
                                data: py
                            });
                            vm.setStores(vmStores);
                        },
                        failure: function (response, opts) {
                            console.log('server-side failure with status code ' + response.status);
                        }
                    });
                }
            }]
        }, {
            xtype: 'dataview',
            tpl: pinyinTpl,
            itemSelector: 'div.pinyin-item',
            bind: {store: '{pinyinStore}'},
            listeners: {
                'refresh': function () {
                    var pinyins = Ext.select('.pinyin-item');
                    Ext.each(pinyins.elements, function (el) {
                        // 第一步,创建可拖动的元素
                        var dd = Ext.create('Ext.dd.DD', el, 'pyDDGroup', {
                            isTarget: false
                        });
                        Ext.apply(dd, overrides);
                    })
                }
            }
        }, {
            // 拼音的拖拽区域
            xtype: 'dataview',
            tpl: fontTpl,
            bind: {store: '{fontStore}'},
            itemSelector: 'div.thumb-wrap',
            emptyText: '<div class="font-none-data">暂时还没有任何文字哦</div>',
            listeners: {
                'refresh': function () {
                    var thumbWrap = Ext.select('.thumb-wrap');
                    // 第二步,创建可以用于接收的元素
                    Ext.each(thumbWrap.elements, function (el) {
                        var thumbWrapDDTarget = Ext.create('Ext.dd.DDTarget', el, 'pyDDGroup', {
                            ignoreSelf: false
                        });
                    })
                },
                'itemclick': function(thisViewObj, record, item, index, e, eOpts){
                    var clsName = e.target.className;
                    if(clsName === 'reset-this'){
                        // 移除这个元素
                        var pNode = e.target.parentNode;
                        // console.dir(pNode);
                        Ext.get('pinyin_panel').appendChild(pNode);
                    }
                }
            }
        }],
        buttons: [{
            xtype: 'button',
            text: '确定'
        }, {
            xtype: 'button',
            text: '取消',
            handler: function () {
                this.up('window').destroy();
            }
        }]
    }]
});

iPhone设备

物理分辨率是硬件所支持的,逻辑分辨率是软件可以达到的。

代数 设备 操作系统 逻辑分辨率(point) 物理分辨率(pixel) 屏幕尺寸(对角线长度) 缩放因子
iPhone
第一代 iPhone 2G iOS 1 320 x 480 480 x 320 3.5寸 1x
第二代 iPhone 3 iOS 2 320 x 480 480 x 320 3.5寸 1x
第三代 iPhone 3GS iOS 3 320 x 480 480 x 320 3.5寸 1x
第四代 iPhone 4 iOS 4 320 x 480 960 × 640 3.5寸 2x
第五代 iPhone 4S iOS 5 320 x 480 960 × 640 3.5寸 2x
第六代 iPhone 5 iOS 6 320 x 568 1136 x 640 4.0寸 2x
第七代 iPhone 5S/5C iOS 7 320 x 568 1136 x 640 4.0寸 2x
第八代 iPhone 6 iOS 8 375 x 667 1334 x 750 4.7寸 2x
第八代 iPhone 6 Plus iOS 8 414 x 736 2208 x 1242 (1920×1080) 5.5寸 3x
第九代 iPhone 6S iOS 9 375 x 667 1334 x 750 4.7寸 2x
第九代 iPhone 6S Plus iOS 9 414 x 736 2208 x 1242 (1920×1080) 5.5寸 3x
iPhone SE iOS 9 320 x 568 1136 x 640 4寸 2x
第十代 iPhone 7 iOS 10 375 x 667 1334 x 750 4.7寸 2x
第十代 iPhone 7 Plus iOS 10 414 x 736 2208 x 1242 (1920×1080) 5.5寸 3x

如图所示:

这里写图片描述

Tips:

机型 屏幕宽高比
iPhone 5 320÷568=0.563
iPhone 6 375÷667=0.562
iPhone 6 Plus 414÷736=0.5625

最终发现iPhone5和6一个小秘密,它们的比例是不变的

iPod设备

设备 操作系统 物理分辨率(pixel) 屏幕尺寸(对角线长度) 缩放因子
iPod Touch 1 iOS 1 480 x 320 3.5寸 1x
iPod Touch 2 iOS 2 480 x 320 3.5寸 1x
iPod Touch 3 iOS 3 480 x 320 3.5寸 1x
iPod Touch 4 iOS 4 960 x 640 3.5寸 2x
iPod Touch 5 iOS 6 1136 x 640 4.0寸 2x
iPod Touch 6 iOS 8 1136 x 640 4.0寸 2x

iPad设备

设备 操作系统 物理分辨率(pixel) 屏幕尺寸(对角线长度) 缩放因子
iPad 1 iOS 3 1024 x 768 9.7寸 1x
iPad 2 iOS 4 1024 x 768 9.7寸 1x
iPad 3 (The New iPad) iOS 5 2048 x 1536 9.7寸 2x
iPad 4 iOS 6 2048 x 1536 9.7寸 2x
iPad Air iOS 7 2048 x 1536 9.7寸 2x
iPad Air 2 iOS 8 2048 x 1536 9.7寸 2x
iPad Pro iOS 9 2732 x 2048 12.9寸 3x
iPad mini iOS 6 1024 x 768 7.9寸 1x
iPad mini 2 iOS 7 2048 x 1536 7.9寸 2x
iPad mini 3 iOS 8 2048 x 1536 7.9寸 2x
iPad mini 4 iOS 9 2048 x 1536 7.9寸 2x

(经常需要查询的设备像素比:转载自 http://blog.csdn.net/scorpio_27/article/details/52297643)

代码写了有一段时间了,依然还是最喜欢的前端工程师。前端也一直在推崇出新各种新技术、框架、架构思想。到了30岁这个尴尬的年龄势必面临着转型的问题。最近也接触了不少新的玩意儿,还是觉得好好做技术才是自己喜欢的东西。我不太同意普遍说的IT工程师到了30岁转到管理岗,反倒是最近这么些年好多公司对工程师,特别是有经验的工程师需求越来越旺盛,薪资水平甚至超过了大多的管理岗。这得益于李总理和国家政策大力推广互联网,也是顺应了时代发展的脚步,现在应该就是工程师们最好黄金时代。

接下来,应该还是会在技术的路上走两年,好好把系统架构,分布式,各种调优工作好好补习补习,补充一些算法和数据库的知识。好了,就这样吧,近期也挺忙的一直没时间更新博客,搞定产品的迭代版本后,把AngularJS的一些使用心得拿出来分享一下。

知乎上的WebPack三连击,非常实用的东西。分享一下。

  • Webpack傻瓜式指南(一)http://zhuanlan.zhihu.com/FrontendMagazine/20367175
  • Webpack傻瓜式指南(二)http://zhuanlan.zhihu.com/FrontendMagazine/20397902
  • Webpack傻瓜式指南(三)http://zhuanlan.zhihu.com/FrontendMagazine/20522487

 

Sass 使用中有一个 Amazing 的功能,配合着 compass 能够做出非常多高效的工作,首当其冲的就是自动化雪碧图工具。

雪碧图自然不用多说,做过前端的开发都知道雪碧图。好处呢在于能够大大节约网络的开销,把Http请求降低。自然在原来的前端开发中,制作的成本也是偏高。环境安装请看这里?http://www.html-js.com/article/CSS-solves-the-installation-and-debugging-of-SASSCompass-scheme

环境Ok之后,通过命令确认安装完毕。

1

创建工程请执行 compass create app

2

Continue reading

前端日常工作是什么?拿到PSD图,然后开切!首先我们建立一个app的文件夹,然后在app下,我们建立img文件夹,接下来是 css …… 项目终于搞定之后,我们要使用在线压缩工具把样式表进行合并压缩,把所有的大尺寸图片重新导入Ps,保存为 web 格式,然后调整质量 。。。。。。

你就没想过偷懒的办法么?你就是这么把时间耗在没有意义的简单机械的活儿中么?

或者,很多前端都已经开始使用Grunt这样的工具了,自动压缩、自动打包、文件合并、Less,Sass 编译、文件监控、本地文件服务器调试环境。不错,基于Grunt另外推出了非常多的 generate ,类似 angularJS , Ember , Backbone, webapp, bootstrap 等等等等,但是 Grunt 的学习曲线也是非常陡的。虽然能够通过一些通用脚手架来搭建前端工程作为基础架构。一旦需要修改配置,则是异常痛苦。

gulp 的功效和 Grunt 也是异曲同工、插件也是异常丰富。

通过 Grunt 与 gulp 的对比。我个人还是比较喜欢 gulp 的一些操作模式。不管是从词法、编程语义的理解程度来说。gulp 都是优于 Grunt 。官网的介绍谈到了文件流、以及处理速度。因为没有现成的案例,所以不能妄自评论。下面就浅谈一下两者之间的使用细节。

这里引用?http://markgoodyear.com/2014/01/getting-started-with-gulp/ 中的例子:

Grunt

sass: {
  dist: {
    options: {
      style: 'expanded'
    },
    files: {
      'dist/assets/css/main.css': 'src/styles/main.scss',
    }
  }
},

autoprefixer: {
  dist: {
    options: {
      browsers: [
        'last 2 version', 'safari 5', 'ie 8', 'ie 9', 'opera 12.1', 'ios 6', 'android 4'
      ]
    },
    src: 'dist/assets/css/main.css',
    dest: 'dist/assets/css/main.css'
  }
},

grunt.registerTask('styles', ['sass', 'autoprefixer']);

Gulp

gulp.task('sass', function() {
  return gulp.src('src/styles/main.scss')
    .pipe(sass({ style: 'compressed' }))
    .pipe(autoprefixer('last 2 version', 'safari 5', 'ie 8', 'ie 9', 'opera 12.1', 'ios 6', 'android 4'))
    .pipe(gulp.dest('dist/assets/css'))
});

不管从代码还是从阅读性上来说,Gulp 不是更加优越一些嘛?从代码的输入和输出,以及事件的操作顺序一幕了然,完全不用在复杂的GruntFile中间去找各种各样的变量表达式。妈妈再也不用担心我的学习!

那么我们来一个简单的Case吧。

首先确保你安装了NodeJS。

然后运行Continue reading