HBuilderX

HBuilderX

极客开发工具
uni-app

uni-app

开发一次,多端覆盖
uniCloud

uniCloud

云开发平台
HTML5+

HTML5+

增强HTML5的功能体验
MUI

MUI

上万Star的前端框架

做聊天页面scroll,页面无法滑动有关之坑,经验之谈分享一下

聊天功能相关实现了,但是页面一进去就无法滑动,发送一条消息才会好,滚动也卡顿。需求是进入聊天动态加载记录应该定位到底部。
一、聊天内容块外面套上框架的滚动div
<div class="mui-scroll-wrapper"style="padding:50px 0">
<div class="mui-scroll">
<div id='你的内容'> </div>
</div>
</div>
二、js中初始化scroll控件。
mui('.mui-scroll-wrapper').scroll({deceleration: 0.0005 });
三、加载好数据后,后面调用这两个方法,一定要调用reLayout,害人不浅!
mui('.mui-scroll-wrapper').scroll().reLayout();
mui('.mui-scroll-wrapper').scroll().scrollToBottom(1000);//毫秒,滚动到底部需要的时间,可自定义。
搞定,滑动和页面初始定位都好用了
之前报错:Ignored attempt to cancel a touchmove event with cancelable=false, for example because scrolling is in progress and cannot be interrupted.这个具体原因算不算控件bug不知道,遇见了也不要方,该包的包起来就行了。
(os:之前的坑还有很多,但是没有记录过,以后再遇见奇葩and一搜索不知所云的问题还会来分享的~)

继续阅读 »

聊天功能相关实现了,但是页面一进去就无法滑动,发送一条消息才会好,滚动也卡顿。需求是进入聊天动态加载记录应该定位到底部。
一、聊天内容块外面套上框架的滚动div
<div class="mui-scroll-wrapper"style="padding:50px 0">
<div class="mui-scroll">
<div id='你的内容'> </div>
</div>
</div>
二、js中初始化scroll控件。
mui('.mui-scroll-wrapper').scroll({deceleration: 0.0005 });
三、加载好数据后,后面调用这两个方法,一定要调用reLayout,害人不浅!
mui('.mui-scroll-wrapper').scroll().reLayout();
mui('.mui-scroll-wrapper').scroll().scrollToBottom(1000);//毫秒,滚动到底部需要的时间,可自定义。
搞定,滑动和页面初始定位都好用了
之前报错:Ignored attempt to cancel a touchmove event with cancelable=false, for example because scrolling is in progress and cannot be interrupted.这个具体原因算不算控件bug不知道,遇见了也不要方,该包的包起来就行了。
(os:之前的坑还有很多,但是没有记录过,以后再遇见奇葩and一搜索不知所云的问题还会来分享的~)

收起阅读 »

模板样式 - NVeiw模板 - wap2app教程

NView模板

NView模板中,控件样式需通过控件标签的style属性定义,类似HTML的内联样式,语法如下:

    <element style="property1:value1;property2:value2;">

NView模板的样式属性是CSS的子集,本文主要介绍NView模板支持的样式定义。

Tips:所有样式定义均需通过标签的style属性设置,目前不支持link外部css文件,也不支持style标签(<style></style>)定义内部样式;

align(对齐)

定义NView控件元素在水平、垂直方向的对齐方式,目前支持的属性包括:

  • align
  • vertical-align

align

控件元素在父容器中的水平对齐方式,可取值:

  • left - 水平居左对齐;
  • center - 水平居中对齐;
  • right - 水平居右对齐

默认值为center。

vertical-align

控件元素在父容器中的垂直对齐方式,可取值:

  • top - 垂直居顶对齐;
  • middle - 垂直居中对齐;
  • bottom - 垂直居底对齐

默认值为middle

background(背景)

定义NView控件元素的背景颜色,目前仅支持background-color一个属性,示例:

    <nview id="nview1" style="background-color: #56C1FF;"></nview>

颜色值有两种方式:

  • 十六进制,如:"#ff0000";
  • RGBA方式,如:"rgba(255,0,0,0.8)"

border(边框)

定义NView控件元素边框的样式和颜色,目前支持的属性包括:

  • border-color
  • border-radius
  • border-width

border-color

控件边框的颜色,可取值:

  • 十六进制,如:"#ff0000";
  • RGBA方式,如:"rgba(255,0,0,0.8)"

默认值为控件的背景颜色值。

border-radius

控件圆角半径像素值,数字加"px"格式字符串,如"5px"。

border-width

控件边框宽度像素值,数字加"px"格式字符串,如"1px"。

font(字体)

定义NView控件元素的文字颜色、带下、加粗等样式,目前支持的属性包括:

  • color
  • font-size
  • font-weight
  • font-style
  • text-overflow
  • white-space

color

文本字体颜色,可取值:

  • 十六进制,如:"#ff0000";
  • RGBA方式,如:"rgba(255,0,0,0.8)"

默认值为"#000000"(黑色)

font-size

文本字体大小,可取值:字体高度像素值,数字加"px"格式字符串,如"12px"。 默认值为"16px"。

font-style

文本字体样式,可取值:

  • normal - 正常字体样式;
  • italic - 斜体样式。

默认值为normal。

font-weight

文本字体粗细,可取值:

  • normal - 普通字体;
  • bold - 粗字体

默认值为normal。

text-overflow

文本内容超出显示区域时处理方式,可取值:

  • clip - 超出显示区域时内容裁剪;
  • ellipsis - 超出显示区域时尾部显示省略标记(...)

默认值为clip。

Tips:text-overflow属性在richtext容器下无效。

white-space

文本换行模式,可取值:

  • nowrap - 不换行,将所有文本在一行中绘制,忽略换行符("\n");
  • normal - 自动换行,当指定的宽度无法绘制所有文本时自动换行绘制,碰到'\n'字符时强制换行。

默认值为nowrap。

position(定位)

定义NView控件元素的位置信息,目前支持的属性包括:

  • top
  • height
  • bottom
  • left
  • width
  • right
  • position

Tips:richtext标签下的元素为流式布局,不支持通过如上属性设置位置信息;

top

NView模板控件元素相对于父容器的向下的偏移量,可取值:

  • 像素值,如"100px";
  • 百分比,如"10%",相对于父容器的高度自动计算;

height

NView模板控件元素的高度,可取值:

  • 像素值,如"100px";
  • 百分比,如"10%",相对于父容器的高度进行计算

bottom

NView模板控件元素相对于父容器向上的偏移量,可取值:

像素值,如"100px";
百分比,如"10%",相对于父容器的高度计算。

控件元素的垂直位置,可以通过top/height/bottom三个属性共同确定,其中:

  • 当设置了top和height值时,忽略bottom属性值;
  • 当未设置top值时,可通过bottom和height属性值来确定垂直位置;
  • 当未设置height值时,可通过top和bottom属性值来确定元素的高度

left

NView模板控件元素相对于父容器的向右的偏移量,可取值:

  • 像素值,如"100px";
  • 百分比,如"10%",相对于父容器的宽度自动计算;

width

NView模板控件元素的宽度,可取值:

  • 像素值,如"100px";
  • 百分比,如"10%",相对于父容器的宽度进行计算

right

NView模板控件元素相对于父容器向左的偏移量,可取值:

  • 像素值,如"100px";
  • 百分比,如"10%",相对于父容器的宽度

控件元素的水平位置,可以通过left/width/right三个属性共同确定,其中:

  • 当设置了left和width值时,忽略right属性值;
  • 当未设置left值时,可通过right和width属性值来确定水平位置;
  • 当未设置width值时,可通过left和right属性值来确定元素的宽度

position

nview控件在webview中的排版方式,可取值:

  • static - View控件在页面中正常定位,如果页面存在滚动条,则View控件跟随窗口内容一起滚动;
  • absolute - Veiw控件在页面中绝对定位,如果页面存在滚动条,View控件显示在屏幕固定位置,不随窗口内容滚动,常用于底部选项卡、购物车等固定显示在特定位置的场景;

注意:position属性仅对<nview>标签元素有用,区别于css中的position属性,NView模板中的position仅表示NView控件是否随着webview页面内容一起滚动,默认为static(随着webview一起滚动);在static排版方式下,依然可以通过坐标位置确定NView控件的位置信息。

继续阅读 »

NView模板中,控件样式需通过控件标签的style属性定义,类似HTML的内联样式,语法如下:

    <element style="property1:value1;property2:value2;">

NView模板的样式属性是CSS的子集,本文主要介绍NView模板支持的样式定义。

Tips:所有样式定义均需通过标签的style属性设置,目前不支持link外部css文件,也不支持style标签(<style></style>)定义内部样式;

align(对齐)

定义NView控件元素在水平、垂直方向的对齐方式,目前支持的属性包括:

  • align
  • vertical-align

align

控件元素在父容器中的水平对齐方式,可取值:

  • left - 水平居左对齐;
  • center - 水平居中对齐;
  • right - 水平居右对齐

默认值为center。

vertical-align

控件元素在父容器中的垂直对齐方式,可取值:

  • top - 垂直居顶对齐;
  • middle - 垂直居中对齐;
  • bottom - 垂直居底对齐

默认值为middle

background(背景)

定义NView控件元素的背景颜色,目前仅支持background-color一个属性,示例:

    <nview id="nview1" style="background-color: #56C1FF;"></nview>

颜色值有两种方式:

  • 十六进制,如:"#ff0000";
  • RGBA方式,如:"rgba(255,0,0,0.8)"

border(边框)

定义NView控件元素边框的样式和颜色,目前支持的属性包括:

  • border-color
  • border-radius
  • border-width

border-color

控件边框的颜色,可取值:

  • 十六进制,如:"#ff0000";
  • RGBA方式,如:"rgba(255,0,0,0.8)"

默认值为控件的背景颜色值。

border-radius

控件圆角半径像素值,数字加"px"格式字符串,如"5px"。

border-width

控件边框宽度像素值,数字加"px"格式字符串,如"1px"。

font(字体)

定义NView控件元素的文字颜色、带下、加粗等样式,目前支持的属性包括:

  • color
  • font-size
  • font-weight
  • font-style
  • text-overflow
  • white-space

color

文本字体颜色,可取值:

  • 十六进制,如:"#ff0000";
  • RGBA方式,如:"rgba(255,0,0,0.8)"

默认值为"#000000"(黑色)

font-size

文本字体大小,可取值:字体高度像素值,数字加"px"格式字符串,如"12px"。 默认值为"16px"。

font-style

文本字体样式,可取值:

  • normal - 正常字体样式;
  • italic - 斜体样式。

默认值为normal。

font-weight

文本字体粗细,可取值:

  • normal - 普通字体;
  • bold - 粗字体

默认值为normal。

text-overflow

文本内容超出显示区域时处理方式,可取值:

  • clip - 超出显示区域时内容裁剪;
  • ellipsis - 超出显示区域时尾部显示省略标记(...)

默认值为clip。

Tips:text-overflow属性在richtext容器下无效。

white-space

文本换行模式,可取值:

  • nowrap - 不换行,将所有文本在一行中绘制,忽略换行符("\n");
  • normal - 自动换行,当指定的宽度无法绘制所有文本时自动换行绘制,碰到'\n'字符时强制换行。

默认值为nowrap。

position(定位)

定义NView控件元素的位置信息,目前支持的属性包括:

  • top
  • height
  • bottom
  • left
  • width
  • right
  • position

Tips:richtext标签下的元素为流式布局,不支持通过如上属性设置位置信息;

top

NView模板控件元素相对于父容器的向下的偏移量,可取值:

  • 像素值,如"100px";
  • 百分比,如"10%",相对于父容器的高度自动计算;

height

NView模板控件元素的高度,可取值:

  • 像素值,如"100px";
  • 百分比,如"10%",相对于父容器的高度进行计算

bottom

NView模板控件元素相对于父容器向上的偏移量,可取值:

像素值,如"100px";
百分比,如"10%",相对于父容器的高度计算。

控件元素的垂直位置,可以通过top/height/bottom三个属性共同确定,其中:

  • 当设置了top和height值时,忽略bottom属性值;
  • 当未设置top值时,可通过bottom和height属性值来确定垂直位置;
  • 当未设置height值时,可通过top和bottom属性值来确定元素的高度

left

NView模板控件元素相对于父容器的向右的偏移量,可取值:

  • 像素值,如"100px";
  • 百分比,如"10%",相对于父容器的宽度自动计算;

width

NView模板控件元素的宽度,可取值:

  • 像素值,如"100px";
  • 百分比,如"10%",相对于父容器的宽度进行计算

right

NView模板控件元素相对于父容器向左的偏移量,可取值:

  • 像素值,如"100px";
  • 百分比,如"10%",相对于父容器的宽度

控件元素的水平位置,可以通过left/width/right三个属性共同确定,其中:

  • 当设置了left和width值时,忽略right属性值;
  • 当未设置left值时,可通过right和width属性值来确定水平位置;
  • 当未设置width值时,可通过left和right属性值来确定元素的宽度

position

nview控件在webview中的排版方式,可取值:

  • static - View控件在页面中正常定位,如果页面存在滚动条,则View控件跟随窗口内容一起滚动;
  • absolute - Veiw控件在页面中绝对定位,如果页面存在滚动条,View控件显示在屏幕固定位置,不随窗口内容滚动,常用于底部选项卡、购物车等固定显示在特定位置的场景;

注意:position属性仅对<nview>标签元素有用,区别于css中的position属性,NView模板中的position仅表示NView控件是否随着webview页面内容一起滚动,默认为static(随着webview一起滚动);在static排版方式下,依然可以通过坐标位置确定NView控件的位置信息。

收起阅读 »

HBuilder 图片压缩,截取

图片压缩 图片裁剪

贡献一下代码

<!DOCTYPE html>  
<html>  

    <head>  
        <meta charset="utf-8">  
        <meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no" />  
        <title></title>  

        <script type="text/javascript" src="js/jquery-1.11.2.min.js" ></script>  

        <script src="js/mui.min.js"></script>  
        <link href="css/mui.min.css" rel="stylesheet" />  
        <script type="text/javascript" charset="utf-8">  
            mui.init();  
        </script>  
    </head>  

    <body>  
        <header class="mui-bar mui-bar-nav">  
            <a class="mui-action-back mui-icon mui-icon-left-nav mui-pull-left"></a>  
            <h1 class="mui-title">起始页</h1>  
        </header>  
        <div class="mui-content">  
            <button id="primary" class="mui-btn-primary mui-btn-block" style="margin-top: 20px;">拍照</button>  
            <button id="success" class="mui-btn-success mui-btn-block" style="margin-top: 20px;">打开相册</button>  
        </div>  

        <div id='dkj-result'>  

        </div>  

        <script>  
            if (window.plus) {  
                plusReady();  
            } else {  
                document.addEventListener('plusready', plusReady, false);  
            }  

            function plusReady() {  
                mui(".mui-content").on("tap", "#primary", function() {  
                    var cmr = plus.camera.getCamera();  
                    cmr.captureImage(function(p) {  
                        plus.io.resolveLocalFileSystemURL(p, function(entry) {  
                            mui.openWindow({  
                                url: 'cropper.html',  
                                id: 'cropper.html',  
                                extras: {  
                                    path: "file:///" + entry.fullPath,  
                                    change: "cem"  
                                }  
                            });  
                        }, function(e) {  
                            mui.toast(e.message);  
                        });  
                    }, function(e) {}, {  
                        filename: "_doc/camera/"  
                    });  
                })  
                mui(".mui-content").on("tap", "#success", function() {  
                    plus.gallery.pick(function(url) {  
                        mui.openWindow({  
                            url: 'cropper.html',  
                            id: 'cropper.html',  
                            extras: {  
                                path: url,  
                                change: "open"  
                            }  
                        });  
                    }, function(error) {  
                        mui.toast("打开相册失败");  
                    });  
                }, false);  
            };  
            window.addEventListener('newsId',function(event){  

                var urlPath = event.detail.urlPath;  

                var sHtml = '<img style="margin: 10px 10px 10px 10px; width: 100px; height: 100px;" src="'+ urlPath +'" />';  
                console.log('返回的参数:' + sHtml);  
                $("#dkj-result").append( sHtml );  
            });  
        </script>  
    </body>  

</html>

第二个文件:

<!DOCTYPE html>  
<html>  

    <head>  
        <meta charset="utf-8">  
        <meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no" />  
        <script src="js/mui.js"></script>  
        <link href="css/mui.min.css" rel="stylesheet" />  
        <link href="css/iconfont.css" rel="stylesheet" />  
        <link rel="stylesheet" href="css/cropper.css" />  

        <script src="js/custom/common.js"></script>  
        <script type="text/javascript" src="js/fastclick.js"></script>  
        <script type="text/javascript" src="js/exif.js"></script>  
        <link href="css/user-information.css" rel="stylesheet" />  
        <script>  
            window.addEventListener('load', function() {  
                FastClick.attach(document.body);  
            }, false);  
        </script>  
    </head>  

    <body>  
        <div id="cropper-example-1">  
            <img id="im" alt="Picture" style="height:100%;width:100%;">  
        </div>  

        <div class="divbut">  
            <div>  
                <p id="quxiao" class="iconfont icon-quxiao"></p>  
            </div>  
            <div>  
                <p id="xuanqu" class="iconfont icon-queding"></p>  
            </div>  
        </div>  
        <div id="test" style="position: absolute;"></div>  
    </body>  
    <script type="text/javascript" src="js/jquery-1.11.2.min.js"></script>  
    <script src="js/cropper.min.js"></script>  
    <!--script src="js/cropper-img.js"></script -->  
    <script type="text/javascript">  

        /*$(function() {  
            ! function() {  
                var i = {  
                    aspectRatio: 1 / 1  
                };  
            }()  
        });*/  
        (function(c) {  

            var Cro = function() {}  
            c.extend(Cro.prototype, {  
                orientation: null,  
                simg: null,  
                simg2: null,  
                urldata: null,  
                view: null,  
                num: 0,  
                sbx: null,  
                sby: null,  
                n: 0,  
                imgurl: null,  
                weizhi: null,  
                imageData: null,  
                onReady: function() {  
                    var that = this;  
                    mui.init();  
                    that.bindEvent();  
                    that.view = plus.webview.currentWebview();  

                    that.simg = document.createElement("img");  
                    that.simg.setAttribute("id", "simg");  
                    document.body.appendChild(that.simg);  

                    var url = that.view.path;  
                    var img = document.createElement("img");  
                    img.setAttribute("src", url);  

                    console.log('原始截取图片路径: ' + url);  

                    //判断图片是否已经加载完,如果已经加载完,则执行下面的函数。  
                    img.addEventListener("load", function() {  

                        EXIF.getData(img, function() {  //调用EXIF解决,图片旋转90的问题  

                            var orientation = EXIF.getAllTags(this).Orientation;  

                            //本地压缩图片。  
                            that.loadcopyImg( img, orientation, url );  

                        });  
                    })  
                },  
                cropperImg: function() {    //加载图片截取框  
                    var that = this;  

                    $('#cropper-example-1 > img').cropper({  
                        aspectRatio: 1 / 1,     //长宽的比例  
                        autoCropArea: 0.5,      //拆减图片框框的大小  
                        strict: true,           //默认值true。 在strict模式中,canvas不能小于容器,剪裁容器不能再canvas之外。  
                        background: false,      //是否在容器上显示网格背景。  
                        guides: true,           //是否在剪裁框上显示虚线  
                        highlight: false,       //是否在剪裁框上显示白色的模态窗口。  
                        dragCrop: false,        //是否允许移除当前的剪裁框,并通过拖动来新建一个剪裁框区域。  
                        movable: false,         //是否允许移动剪裁框。  
                        resizable: false,       //是否允许改变剪裁框的大小。  
                        crop: function(data) {  

                            that.weizhi = data; //将当前截取区域的位置 和 大小放入全局变量中  

                            that.imageData = $('#cropper-example-1 > img').cropper('getImageData'); //将当前图片的信息放入全局参数中去  

                            console.log('getImageData: ' + JSON.stringify( that.imageData ) );  
                            console.log('weizhi: ' + JSON.stringify( that.weizhi ));  
                        }  
                    });  
                },  
                loadcopyImg: function(img, opt,url) {   //本地处理图片。将图片进行压缩  
                    var that = this;  

                    var name="_doc/upload/F_ZDDZZ_"+ ( new Date() ).valueOf() +".jpg"; //拼装唯一图片值    
                    plus.zip.compressImage({  
                            src:url,        //src: (String 类型 )压缩转换原始图片的路径    
                            dst:name,       //压缩转换目标图片的路径    
                            quality:20,     //quality: (Number 类型 )压缩图片的质量.取值范围为1-100    
                            overwrite:true  //overwrite: (Boolean 类型 )覆盖生成新文件    
                        },    
                        function(event) {  
                            var path = name;            //压缩转换目标图片的路径  
                            console.log( '本地压缩图片成功:' + event.target);  

                            that.imgurl = event.target; //将压缩后的图片放入全局变量中  

                            $("#im").attr("src", event.target );    //显示压缩后的图片  
                            //$("#im").attr("src", 'file:///var/mobile/Containers/Data/Application/67789E0B-1848-4491-A225-E25B1C645A26/Documents/Pandora/apps/HBuilder/doc/upload/F_cutOut_1504768316962.jpg');  
                            that.cropperImg();  //调用截取框  

                        },function(error) {    
                            plus.nativeUI.toast("压缩图片失败,请稍候再试");    
                    });  

                },  
                bindEvent: function() {                 //绑定事件  
                    document.getElementById("quxiao").addEventListener("click", function() {  
                        window.cro.view.close();  
                    });  
                    document.getElementById("xuanqu").addEventListener("click", function() {  
                        //window.cro.showFace(window.cro.urldata);  
                        window.cro.jieQu();  
                    });  
                },  
                jieQu: function(){  
                    console.log('截取开始');  
                    var date = (new Date()).valueOf();  //时间戳,避免截图后的文件名相同  
                    var that = this;  

                    //计算各个数值的百分比  
                    var top_Precentage = ( that.weizhi.y / that.imageData.naturalHeight ).toFixed(2) * 100 + '%';  
                    var left_Precentage = ( that.weizhi.x / that.imageData.naturalWidth ).toFixed(2) * 100 + '%';  
                    var width_Precentage = ( that.weizhi.width / that.imageData.naturalWidth ).toFixed(2) * 100 + '%';  
                    var height_Precentage = ( that.weizhi.height / that.imageData.naturalHeight ).toFixed(2) * 100 + '%';  

                    console.log( top_Precentage + " | " + left_Precentage + " | " + width_Precentage + " | " + height_Precentage );  
                    //通过压缩按比例截取图片  
                    plus.zip.compressImage({  
                        src: that.imgurl,   //src在这里是第一步Url里的src。也就是本地路径  
                        dst: '_doc/upload/F_cutOut_'+ date +'.jpg',  
                        overwrite: true,  
                        clip: {  
                          top: top_Precentage,   
                          left: left_Precentage,  
                          width: width_Precentage,  
                          height: height_Precentage   
                        }   
                      },  
                      function( event ) {  
                        console.log('截取后的图片路径:' + event.target);  

                        var vNextPage = plus.webview.getWebviewById( 'home.html' );  
                        if( vNextPage != null ){  

                            mui.fire( vNextPage, 'newsId',{  
                                urlPath : event.target  
                            });  
                        }else{  
                            console.log( '跳转失败 ');  
                        }  

                        window.cro.view.close();/**/  
                        $("#im").attr("src", event.target ); //压缩图片  
                      }  
                    );  
                }  

            });  
            window.cro = new Cro();  
            c.plusReady(function() {  
                window.cro.onReady();  
            })  
        })(mui)  
    </script>  

</html>

继续阅读 »

贡献一下代码

<!DOCTYPE html>  
<html>  

    <head>  
        <meta charset="utf-8">  
        <meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no" />  
        <title></title>  

        <script type="text/javascript" src="js/jquery-1.11.2.min.js" ></script>  

        <script src="js/mui.min.js"></script>  
        <link href="css/mui.min.css" rel="stylesheet" />  
        <script type="text/javascript" charset="utf-8">  
            mui.init();  
        </script>  
    </head>  

    <body>  
        <header class="mui-bar mui-bar-nav">  
            <a class="mui-action-back mui-icon mui-icon-left-nav mui-pull-left"></a>  
            <h1 class="mui-title">起始页</h1>  
        </header>  
        <div class="mui-content">  
            <button id="primary" class="mui-btn-primary mui-btn-block" style="margin-top: 20px;">拍照</button>  
            <button id="success" class="mui-btn-success mui-btn-block" style="margin-top: 20px;">打开相册</button>  
        </div>  

        <div id='dkj-result'>  

        </div>  

        <script>  
            if (window.plus) {  
                plusReady();  
            } else {  
                document.addEventListener('plusready', plusReady, false);  
            }  

            function plusReady() {  
                mui(".mui-content").on("tap", "#primary", function() {  
                    var cmr = plus.camera.getCamera();  
                    cmr.captureImage(function(p) {  
                        plus.io.resolveLocalFileSystemURL(p, function(entry) {  
                            mui.openWindow({  
                                url: 'cropper.html',  
                                id: 'cropper.html',  
                                extras: {  
                                    path: "file:///" + entry.fullPath,  
                                    change: "cem"  
                                }  
                            });  
                        }, function(e) {  
                            mui.toast(e.message);  
                        });  
                    }, function(e) {}, {  
                        filename: "_doc/camera/"  
                    });  
                })  
                mui(".mui-content").on("tap", "#success", function() {  
                    plus.gallery.pick(function(url) {  
                        mui.openWindow({  
                            url: 'cropper.html',  
                            id: 'cropper.html',  
                            extras: {  
                                path: url,  
                                change: "open"  
                            }  
                        });  
                    }, function(error) {  
                        mui.toast("打开相册失败");  
                    });  
                }, false);  
            };  
            window.addEventListener('newsId',function(event){  

                var urlPath = event.detail.urlPath;  

                var sHtml = '<img style="margin: 10px 10px 10px 10px; width: 100px; height: 100px;" src="'+ urlPath +'" />';  
                console.log('返回的参数:' + sHtml);  
                $("#dkj-result").append( sHtml );  
            });  
        </script>  
    </body>  

</html>

第二个文件:

<!DOCTYPE html>  
<html>  

    <head>  
        <meta charset="utf-8">  
        <meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no" />  
        <script src="js/mui.js"></script>  
        <link href="css/mui.min.css" rel="stylesheet" />  
        <link href="css/iconfont.css" rel="stylesheet" />  
        <link rel="stylesheet" href="css/cropper.css" />  

        <script src="js/custom/common.js"></script>  
        <script type="text/javascript" src="js/fastclick.js"></script>  
        <script type="text/javascript" src="js/exif.js"></script>  
        <link href="css/user-information.css" rel="stylesheet" />  
        <script>  
            window.addEventListener('load', function() {  
                FastClick.attach(document.body);  
            }, false);  
        </script>  
    </head>  

    <body>  
        <div id="cropper-example-1">  
            <img id="im" alt="Picture" style="height:100%;width:100%;">  
        </div>  

        <div class="divbut">  
            <div>  
                <p id="quxiao" class="iconfont icon-quxiao"></p>  
            </div>  
            <div>  
                <p id="xuanqu" class="iconfont icon-queding"></p>  
            </div>  
        </div>  
        <div id="test" style="position: absolute;"></div>  
    </body>  
    <script type="text/javascript" src="js/jquery-1.11.2.min.js"></script>  
    <script src="js/cropper.min.js"></script>  
    <!--script src="js/cropper-img.js"></script -->  
    <script type="text/javascript">  

        /*$(function() {  
            ! function() {  
                var i = {  
                    aspectRatio: 1 / 1  
                };  
            }()  
        });*/  
        (function(c) {  

            var Cro = function() {}  
            c.extend(Cro.prototype, {  
                orientation: null,  
                simg: null,  
                simg2: null,  
                urldata: null,  
                view: null,  
                num: 0,  
                sbx: null,  
                sby: null,  
                n: 0,  
                imgurl: null,  
                weizhi: null,  
                imageData: null,  
                onReady: function() {  
                    var that = this;  
                    mui.init();  
                    that.bindEvent();  
                    that.view = plus.webview.currentWebview();  

                    that.simg = document.createElement("img");  
                    that.simg.setAttribute("id", "simg");  
                    document.body.appendChild(that.simg);  

                    var url = that.view.path;  
                    var img = document.createElement("img");  
                    img.setAttribute("src", url);  

                    console.log('原始截取图片路径: ' + url);  

                    //判断图片是否已经加载完,如果已经加载完,则执行下面的函数。  
                    img.addEventListener("load", function() {  

                        EXIF.getData(img, function() {  //调用EXIF解决,图片旋转90的问题  

                            var orientation = EXIF.getAllTags(this).Orientation;  

                            //本地压缩图片。  
                            that.loadcopyImg( img, orientation, url );  

                        });  
                    })  
                },  
                cropperImg: function() {    //加载图片截取框  
                    var that = this;  

                    $('#cropper-example-1 > img').cropper({  
                        aspectRatio: 1 / 1,     //长宽的比例  
                        autoCropArea: 0.5,      //拆减图片框框的大小  
                        strict: true,           //默认值true。 在strict模式中,canvas不能小于容器,剪裁容器不能再canvas之外。  
                        background: false,      //是否在容器上显示网格背景。  
                        guides: true,           //是否在剪裁框上显示虚线  
                        highlight: false,       //是否在剪裁框上显示白色的模态窗口。  
                        dragCrop: false,        //是否允许移除当前的剪裁框,并通过拖动来新建一个剪裁框区域。  
                        movable: false,         //是否允许移动剪裁框。  
                        resizable: false,       //是否允许改变剪裁框的大小。  
                        crop: function(data) {  

                            that.weizhi = data; //将当前截取区域的位置 和 大小放入全局变量中  

                            that.imageData = $('#cropper-example-1 > img').cropper('getImageData'); //将当前图片的信息放入全局参数中去  

                            console.log('getImageData: ' + JSON.stringify( that.imageData ) );  
                            console.log('weizhi: ' + JSON.stringify( that.weizhi ));  
                        }  
                    });  
                },  
                loadcopyImg: function(img, opt,url) {   //本地处理图片。将图片进行压缩  
                    var that = this;  

                    var name="_doc/upload/F_ZDDZZ_"+ ( new Date() ).valueOf() +".jpg"; //拼装唯一图片值    
                    plus.zip.compressImage({  
                            src:url,        //src: (String 类型 )压缩转换原始图片的路径    
                            dst:name,       //压缩转换目标图片的路径    
                            quality:20,     //quality: (Number 类型 )压缩图片的质量.取值范围为1-100    
                            overwrite:true  //overwrite: (Boolean 类型 )覆盖生成新文件    
                        },    
                        function(event) {  
                            var path = name;            //压缩转换目标图片的路径  
                            console.log( '本地压缩图片成功:' + event.target);  

                            that.imgurl = event.target; //将压缩后的图片放入全局变量中  

                            $("#im").attr("src", event.target );    //显示压缩后的图片  
                            //$("#im").attr("src", 'file:///var/mobile/Containers/Data/Application/67789E0B-1848-4491-A225-E25B1C645A26/Documents/Pandora/apps/HBuilder/doc/upload/F_cutOut_1504768316962.jpg');  
                            that.cropperImg();  //调用截取框  

                        },function(error) {    
                            plus.nativeUI.toast("压缩图片失败,请稍候再试");    
                    });  

                },  
                bindEvent: function() {                 //绑定事件  
                    document.getElementById("quxiao").addEventListener("click", function() {  
                        window.cro.view.close();  
                    });  
                    document.getElementById("xuanqu").addEventListener("click", function() {  
                        //window.cro.showFace(window.cro.urldata);  
                        window.cro.jieQu();  
                    });  
                },  
                jieQu: function(){  
                    console.log('截取开始');  
                    var date = (new Date()).valueOf();  //时间戳,避免截图后的文件名相同  
                    var that = this;  

                    //计算各个数值的百分比  
                    var top_Precentage = ( that.weizhi.y / that.imageData.naturalHeight ).toFixed(2) * 100 + '%';  
                    var left_Precentage = ( that.weizhi.x / that.imageData.naturalWidth ).toFixed(2) * 100 + '%';  
                    var width_Precentage = ( that.weizhi.width / that.imageData.naturalWidth ).toFixed(2) * 100 + '%';  
                    var height_Precentage = ( that.weizhi.height / that.imageData.naturalHeight ).toFixed(2) * 100 + '%';  

                    console.log( top_Precentage + " | " + left_Precentage + " | " + width_Precentage + " | " + height_Precentage );  
                    //通过压缩按比例截取图片  
                    plus.zip.compressImage({  
                        src: that.imgurl,   //src在这里是第一步Url里的src。也就是本地路径  
                        dst: '_doc/upload/F_cutOut_'+ date +'.jpg',  
                        overwrite: true,  
                        clip: {  
                          top: top_Precentage,   
                          left: left_Precentage,  
                          width: width_Precentage,  
                          height: height_Precentage   
                        }   
                      },  
                      function( event ) {  
                        console.log('截取后的图片路径:' + event.target);  

                        var vNextPage = plus.webview.getWebviewById( 'home.html' );  
                        if( vNextPage != null ){  

                            mui.fire( vNextPage, 'newsId',{  
                                urlPath : event.target  
                            });  
                        }else{  
                            console.log( '跳转失败 ');  
                        }  

                        window.cro.view.close();/**/  
                        $("#im").attr("src", event.target ); //压缩图片  
                      }  
                    );  
                }  

            });  
            window.cro = new Cro();  
            c.plusReady(function() {  
                window.cro.onReady();  
            })  
        })(mui)  
    </script>  

</html>

收起阅读 »

弹出 mui-modal 标题出现显示不正常的问题

modal

<div id="transferModal" class="mui-modal">
<header class="mui-bar mui-bar-nav" style="background-color: #FFFFFF;border-bottom: 1px #F4F4F4 solid;">
<a class="mui-icon mui-pull-left" href="#transferModal">
<img src="../public/img/backArrow_gray19x19@3x.png" width="15" style="margin-left:3px;margin-top:4px;" />
</a>
<h1 class="mui-title">标题</h1>
</header>
<div class="mui-content" style="height: 100%;">
</div>
</div>

比如这个modal 弹出后,发现标题 header 直接没了。各种原因找了,发现加上样式:

.mui-modal header{overflow: hidden;}

就可以解决了。

继续阅读 »

<div id="transferModal" class="mui-modal">
<header class="mui-bar mui-bar-nav" style="background-color: #FFFFFF;border-bottom: 1px #F4F4F4 solid;">
<a class="mui-icon mui-pull-left" href="#transferModal">
<img src="../public/img/backArrow_gray19x19@3x.png" width="15" style="margin-left:3px;margin-top:4px;" />
</a>
<h1 class="mui-title">标题</h1>
</header>
<div class="mui-content" style="height: 100%;">
</div>
</div>

比如这个modal 弹出后,发现标题 header 直接没了。各种原因找了,发现加上样式:

.mui-modal header{overflow: hidden;}

就可以解决了。

收起阅读 »

用vue和mui整合开发的一个图片压缩服务示例

图片裁剪 Vue

前言

这只是个人的经验总结和分享,并不是,也没时间做一个完整的教程。
分享的是我在实际项目开发中碰到的杂七杂八的问题,仅供参考。

一个简单的图片压缩工具

在线地址: 图片压缩工具-友间共享

在工作过程中,难免会碰到需要为同一个图片生成多种分辨率的情形,比如打包app之前需要准备好的各类图标,虽然简单,但是费点时间在所难免,就自己做了一个小工具,主要功能如下:

  1. 可以自定义处理后的图片的分辨率和压缩方式等,自定义的样式自动保存,下次可以重复使用。
  2. 一键上传图片,根据预设的样式批量生成处理结果,根据需要再自行下载

alt

开发环境

  1. vue+mui+webpack+npm+macos
  2. 后台服务: 七牛云存储

运行环境

在线服务,可在支持h5的浏览器上运行,电脑,手机平板理论上都可以

在线地址: 图片压缩工具-友间共享

实现原理

其实很简单,就是先上传图片到七牛云服务
然后在页面上提供配置页面,保存用户配置的格式,然后把每个格式都转化成七牛的图片转化服务的格式化字符串,最后把这个字符串和图片的原地址合并成图片下载链接,就可以在点击链接时调用七牛的图片处理服务,按照我们设定的格式下载转化后的图片。

项目目录结构

alt

** 详细目录结构介绍和开发环境部署参考 怎么搭建vue和Mui的多页面开发环境

主要代码

引入vue,mui等库
import LContext from 'lui/context.js'  
import Vue from 'vue'  
import 'babel-polyfill'  
import Vuex from 'vuex'  
import $ from './mui/mui.min'  

require('../css/mui.min.css')  
require('../css/icons-extra.css')  
//require('../css/iconfont.css')  
//require('../css/app.css')  
require('../css/app.less')  
window.Vue = Vue  
window.Vuex = Vuex  
window.$ = $  
window.mui = $  

require('./mui/mui.pullToRefresh')  
require('./mui/mui.pullToRefresh.material')  

//加载BASE64  
require('exports-loader!./util/base64.js')  
Vue.use(Vuex)  

//import LoginDialog from 'app/Login'  

if(process.env.NODE_ENV === 'production') {  
    Vue.config.productionTip = false  
}

有点偷懒,部分变量其实是可以在环境配置时引入的,这里就先忽略了,反正效果也一样

其中的库可根据需要引入,因为是从我自己的架构上COPY下来的,有些文件和库在这个项目中是可以不用引入的。引用的路径和webpack环境配置有关,以下是我的配置

    resolve: {  
        extensions: ['.js', '.vue', '.json'],  
        alias: { //定义别名,简易方式,可以在require调用时缩短路径  
            'vue$': 'vue/dist/vue.esm.js',  
            '@': resolve('src'),  
            "page$": resolve('src/js/page.js'),  
            'app': resolve('src/components/'),  
            'lui': resolve('src/js/lui/'),  
            'mui': resolve('src/js/mui/'),  
            'util': resolve('src/js/util/')  
        }  
    },
output.js代码
import Page from 'page'  

import Uploader from 'lui/upload.js'  

var formVue = new Vue({  
    el: '.mui-content',  
    data: {  
        src: '',  
        formats: [],  
        formatBuff: {  
            facs: 9,  
            scaleType: '按指定宽高值强行缩略,可能导致目标图片变形,width和height取值范围1-9999'  
        }, //当前正在编辑的图片类型  
        collapsePanels: {  
            scale: false  
        },  
        logined: false  
    },  
    computed: {  
        showScale: function() {  
            switch(this.formatBuff.facs) {  
                case 1:  
                    return true;  
                case 2:  
                    return true;  
                case 3:  
                    return true;  
                default:  
                    break;  
            }  
            return false;  
        },  
        showHeight: function() {  
            switch(this.formatBuff.facs) {  
                case 5:  
                    return true;  

                case 7:  
                    return true;  
                case 8:  
                    return true;  

                case 9:  
                    return true;  

                case 10:  
                    return true;  

                case 11:  
                    return true;  
                default:  
                    break;  
            }  
            return false;  
        },  
        showWidth: function() {  
            switch(this.formatBuff.facs) {  
                case 4:  
                    return true;  
                case 7:  
                    return true;  
                case 8:  
                    return true;  

                case 9:  
                    return true;  
                case 10:  
                    return true;  

                case 11:  
                    return true;  
                default:  
                    break;  
            }  
            return false  
        },  
        showArea: function() {  
            switch(this.formatBuff.facs) {  
                case 12:  
                    return true;  
                default:  
                    break;  
            }  
            return false;  
        },  
        showInput: function() {  
            if(this.formatBuff.facs > 0) {  
                return true  
            }  
            return false  
        },  
        srcUri: function() {  
            if(this.src) {  
                return LContext.fileServer + this.src + LContext.image.Thumbnail  
            }  
            return ''  
        },  
        srcThumbnail: function() {  
            if(this.src) {  
                return LContext.fileServer + this.src +  
                    LContext.image.Thumbnail  
            }  
            return ''  
        },  
        isEdit: function() {  
            if(this.formatBuff.uid) {  
                return true  
            }  
            return false  
        },  
        saveBtnText: function() {  
            console.log(this.isEdit + '  edit')  
            if(this.isEdit) {  
                return '保存修改'  
            } else {  
                return '保存新建样式'  
            }  
        }  
    },  
    methods: {  
        downLoad: function(format) {  
            var _format = this.decodeFormat(format)  
            if(_format.length > 2) {  
                lpage.open(LContext.fileServer + this.src + '?imageMogr2' + _format, undefined, 'new')  
            } else {  
                lpage.alert('下载格式无效!')  
            }  
        },  
        downUrl: function(format) {  
            var _format = this.decodeFormat(format)  
            if(_format.length > 2) {  
                return LContext.fileServer + this.src + '?imageMogr2' + _format  
            } else {  
                return false  
            }  
        },  
        deleteFormat: function(format) {  
            for(var i = 0; i < this.formats.length; i++) {  
                if(this.formats[i].uid === format.uid) {  
                    this.formats.splice(i, 1)  
                    lpage.saveFormats()  
                }  
            }  
        },  
        setFacs: function(facs, event) {  
            //          this.formatBuff.facs = parseInt(facs, 10)  
            Vue.set(this.formatBuff, 'facs', parseInt(facs, 10))  
            Vue.set(this.formatBuff, 'scaleType', event.currentTarget.innerText)  

            Vue.nextTick(function() {  
                this.closePanel('scale')  
            }.bind(this))  
            //          console.log(JSON.stringify(this.formatBuff))  
        },  
        decodeFormat: function(format) {  
            //缩放  
            var formatStr = '/thumbnail/'  
            switch(format.facs) {  
                case 1:  
                    formatStr += '!' + format.scale + 'p'  
                    break;  
                case 2:  
                    formatStr += '!' + format.scale + 'px'  
                    break;  
                case 3:  
                    formatStr += '!x' + format.scale + 'p'  
                    break;  
                case 4:  
                    formatStr += format.width + 'x'  
                    break;  
                case 5:  
                    formatStr += 'x' + format.height  
                    break;  

                case 7:  
                    formatStr += format.width + 'x' + format.height  
                    break;  

                case 8:  
                    formatStr += '!' + format.width + 'x' + format.height  
                    break;  

                case 9:  
                    formatStr += format.width + 'x' + format.height + '!'  
                    break;  

                case 10:  
                    formatStr += format.width + 'x' + format.height + '>'  
                    break;  

                case 11:  
                    formatStr += format.width + 'x' + format.height + '<'  
                    break;  
                case 12:  
                    formatStr += format.area + '@'  
                    break;  
                default:  
                    formatStr = ''  
                    break;  
            }  
            //格式转换  
            if(format.suffix !== 'auto') {  
                formatStr += '/format/' + format.suffix  
            }  
            return formatStr  
        },  
        isActiveFacs: function(facs) {  
            if(this.formatBuff.facs === facs) {  
                return true  
            }  
            return false  
        },  
        panelActive: function(collapseList) {  
            return this.collapsePanels[collapseList]  
        },  
        togglePanel: function(collapseList) {  
            Vue.set(this.collapsePanels, collapseList, !this.collapsePanels[collapseList])  
        },  
        closePanel: function(collapseList) {  
            Vue.set(this.collapsePanels, collapseList, false)  
        },  
        submitNewFormat: function() {  
            var newFormat = {  
                uid: 'format_' + new Date().getTime(),  
                height: this.formatBuff.height,  
                width: this.formatBuff.width,  
                scale: this.formatBuff.scale,  
                area: this.formatBuff.area,  
                facs: this.formatBuff.facs,  
                desc: this.formatBuff.desc,  
                scaleType: this.formatBuff.scaleType,  
                suffix: this.formatBuff.suffix ? this.formatBuff.suffix : 'auto', //图片类型  
                quality: this.formatBuff.quality ? this.formatBuff.quality : 75 //图片质量  
            }  
            var change = false  
            if(this.formatBuff.uid) {  
                for(var i = 0; i < this.formats.length; i++) {  
                    if(this.formats[i].uid === this.formatBuff.uid) {  
                        newFormat.uid = this.formatBuff.uid  
                        this.formatBuff.uid = undefined  
                        Vue.set(this.formats, i, newFormat)  
                        change = true  
                    }  
                }  
            }!change && this.formats.push(newFormat)  
            lpage.saveFormats()  
        },  
        editFormat: function(format) {  
            Vue.set(this.formatBuff, 'uid', format.uid)  
            Vue.set(this.formatBuff, 'height', format.height)  
            Vue.set(this.formatBuff, 'width', format.width)  
            Vue.set(this.formatBuff, 'scale', format.scale)  
            Vue.set(this.formatBuff, 'area', format.area)  
            Vue.set(this.formatBuff, 'facs', format.facs)  
            Vue.set(this.formatBuff, 'desc', format.desc)  
            Vue.set(this.formatBuff, 'scaleType', format.scaleType)  
            Vue.set(this.formatBuff, 'suffix', format.suffix === 'auto' ? undefined : format.suffix)  
            Vue.set(this.formatBuff, 'quality', format.quality ? format.quality : 75)  
        }  
    }  
})  

var pcPage = {  
    initUI: function() {  
        this.initUploader()  
    },  
    initData: function() {  
        //      this.user.isLogin(function(logined) {  
        //          if(logined) {  
        //              formVue.logined = true  
        lpage.getFormats()  
        //          } else {  
        //              formVue.logined = false  
        //              lpage.user.login()  
        //          }  
        //      })  
    },  
    initUploader: function() {  
        new Uploader({  
            selector: 'image_uploader',  
            token: {  
                mode: 'imagescale',  
                image: { //图片  
                    prefix: '/utils/imagescale/'  
                }  
            },  
            success: function(data) {  
                formVue.src = data.key  
                lpage.saveFormats()  
            }  
        }).init()  
    },  
    saveFormats: function() {  
        lpage.setItem('_myFormats', JSON.stringify(formVue.formats))  
        lpage.setItem('_lastSrc', formVue.src)  
    },  
    getFormats: function() {  
        var _fmt = lpage.getItem('_myFormats')  
        if(!_fmt) {  
            _fmt = [{  
                'uid': 'format_1504082048978',  
                'height': '2208',  
                'width': '1242',  
                'scale': '50',  
                'facs': 9,  
                'desc': '苹果IphoneApp审核用的截屏图片1242*2208',  
                'scaleType': '按指定宽高值强行缩略,可能导致目标图片变形,width和height取值范围1-9999\n',  
                'suffix': 'auto',  
                'quality': '100'  
            }, {  
                'uid': 'format_1504144877541',  
                'height': '2732',  
                'width': '2048',  
                'scale': '50',  
                'facs': 9,  
                'desc': '苹果IpadApp审核用的截屏图片2048*2732',  
                'scaleType': '按指定宽高值强行缩略,可能导致目标图片变形,width和height取值范围1-9999\n',  
                'suffix': 'auto',  
                'quality': '100'  
            }]  
            Vue.set(formVue, 'formats', _fmt)  
        } else  

        if(_fmt) {  
            try {  
                _fmt = JSON.parse(_fmt)  
                Vue.set(formVue, 'formats', _fmt)  
            } catch(e) {  
                //TODO handle the exception  
                console.log('err')  
            }  
        }  

        _fmt = lpage.getItem('_lastSrc')  
        if(_fmt) {  
            formVue.src = _fmt  
        }  
    }  
}  

Page.init({  
    pc: pcPage  
})
output.html源码
<!DOCTYPE html>  
<html class="feedback">  

    <head>  
        <meta charset="utf-8">  
        <meta name="viewport" content="width=device-width, initial-scale=1,maximum-scale=1,user-scalable=no">  
        <meta name="apple-mobile-web-app-capable" content="yes">  
        <meta name="apple-mobile-web-app-status-bar-style" content="black">  
        <title>友间共享-图片处理</title>  
        <style>  
            .mui-content .mui-table-view-radio .mui-table-view-cell>a:not(.mui-btn) {  
                text-align: left;  
                white-space: normal;  
            }  
        </style>  
    </head>  

    <body>  
        <header class="mui-bar mui-bar-nav">  
            <a class="mui-action-back mui-btn  mui-btn-link mui-btn-nav mui-pull-left"><span class=" mui-icon mui-icon-left-nav "></span> </a>  
            <h1 class="mui-title">友间共享-图片处理</h1>  
        </header>  

        <div class="mui-content">  

            <p class="mui-content-padded">可将上传的图片一次性转化输出成多个指定高度/宽度的图片,每次上传文件会扣除一定数额的积分,积分可通过多种方式获取  
                <a l-href="https://www.betweenfriends.cn/view/mine/bonusrule.html">积分获取/使用规则</a>  
            </p>  
            <div class="mui-content-padded ">  
                <img :src="srcUri" style="max-width: 250px;" />  
                <div class="image-list">  
                    <div class="image-item space">  
                        <button type="button" class="mui-btn mui-btn-blue">点击上传</button>  
                        <div class="file">  
                            <form id="image_uploader"></form>  
                        </div>  
                    </div>  
                </div>  
            </div>  

            <p class="mui-content-padded">转换后的图片下载</p>  
            <ul class="mui-table-view">  
                <li v-for="format in formats" class="mui-table-view-cell mui-media">  
                    <img class="mui-media-object mui-pull-left" :src="srcThumbnail">  
                    <div class="mui-media-body">  
                        <p>{{format.desc}}</p>  
                        <button type="button" class="mui-btn mui-btn-link" @tap="deleteFormat(format)">删除样式</button>  
                        <button type="button" class="mui-btn mui-btn-link" @tap="editFormat(format)">编辑样式</button>  
                        <a class="mui-btn mui-btn-blue" :href="downUrl(format)" download="格式化图片">下载图片</a>  
                    </div>  
                </li>  
            </ul>  

            <hr />  
            <p class="mui-content-padded" style="margin-top: 20px;">图片处理参数配置,可在此处新增图片样式</p>  
            <ul class="mui-table-view">  

                <li class="mui-table-view-cell mui-collapse" :class="{'mui-active':panelActive('scale')}" @tap="togglePanel('scale')">  
                    <a class="mui-navigate-right">  
                        缩放模式</a>  
                    <ul class="mui-table-view mui-table-view-radio">  

                        <li class="mui-table-view-cell" :class="{'mui-selected':isActiveFacs(9)}" @tap="setFacs(9,$event)">  
                            <a class="mui-navigate-right">  
                                按指定宽高值强行缩略,可能导致目标图片变形,width和height取值范围1-9999  
                            </a>  
                        </li>  

                        <li class="mui-table-view-cell" :class="{'mui-selected':isActiveFacs(1)}" @tap="setFacs(1,$event)">  
                            <a class="mui-navigate-right">  
                                基于原图大小,按指定百分比缩放。Scale取值范围1-999  
                            </a>  
                        </li>  
                        <li class="mui-table-view-cell" :class="{'mui-selected':isActiveFacs(2)}" @tap="setFacs(2,$event)">  
                            <a class="mui-navigate-right">  
                                以百分比形式指定目标图片宽度,高度不变。Scale取值范围1-999  
                            </a>  
                        </li>  
                        <li class="mui-table-view-cell" :class="{'mui-selected':isActiveFacs(3)}" @tap="setFacs(3,$event)">  
                            <a class="mui-navigate-right">  
                                以百分比形式指定目标图片高度,宽度不变。Scale取值范围1-999  
                            </a>  
                        </li>  
                        <li class="mui-table-view-cell" :class="{'mui-selected':isActiveFacs(4)}" @tap="setFacs(4,$event)">  
                            <a class="mui-navigate-right">  
                                指定目标图片宽度,高度等比缩放,Width取值范围1-9999  
                            </a>  
                        </li>  
                        <li class="mui-table-view-cell" :class="{'mui-selected':isActiveFacs(5)}" @tap="setFacs(5,$event)">  
                            <a class="mui-navigate-right">  
                                指定目标图片高度,宽度等比缩放,Height取值范围1-9999  
                            </a>  
                        </li>  

                        <li class="mui-table-view-cell" :class="{'mui-selected':isActiveFacs(7)}" @tap="setFacs(7,$event)">  
                            <a class="mui-navigate-right">  
                                等比缩放,比例值为宽缩放比和高缩放比的较小值,Width 和 Height 取值范围1-9999。 注意:宽缩放比:目标宽/原图宽   高缩放比:目标高/原图高  
                            </a>  
                        </li>  
                        <li class="mui-table-view-cell" :class="{'mui-selected':isActiveFacs(8)}" @tap="setFacs(8,$event)">  
                            <a class="mui-navigate-right">  
                                等比缩放,比例值为宽缩放比和高缩放比的较大值,Width 和 Height 取值范围1-9999。 注意:宽缩放比:目标宽/原图宽   高缩放比:目标高/原图高  
                            </a>  
                        </li>  

                        <li class="mui-table-view-cell" :class="{'mui-selected':isActiveFacs(10)}" @tap="setFacs(10,$event)">  
                            <a class="mui-navigate-right">  
                                等比缩小,比例值为宽缩放比和高缩放比的较小值。如果目标宽和高都大于原图宽和高,则不变,Width 和 Height 取值范围1-9999。 注意:宽缩放比:目标宽/原图宽   高缩放比:目标高/原图高;  
                            </a>  
                        </li>  

                        <li class="mui-table-view-cell" :class="{'mui-selected':isActiveFacs(11)}" @tap="setFacs(11,$event)">  
                            <a class="mui-navigate-right">  
                                等比放大,比例值为宽缩放比和高缩放比的较小值。如果目标宽(高)小于原图宽(高),则不变,Width 和 Height 取值范围1-9999。 注意: 宽缩放比:目标宽/原图宽   高缩放比:目标高/原图高  
                            </a>  
                        </li>  

                        <li class="mui-table-view-cell" :class="{'mui-selected':isActiveFacs(12)}" @tap="setFacs(12,$event)">  
                            <a class="mui-navigate-right">  
                                按原图高宽比例等比缩放,缩放后的像素数量不超过指定值,Area取值范围1-24999999  
                            </a>  
                        </li>  
                    </ul>  
                </li>  
            </ul>  
            <p class="mui-content-padded" v-show="showInput">{{formatBuff.scaleType}}</p>  
            <form class="mui-input-group">  
                <div class="mui-input-row" v-show="showScale">  
                    <label>缩放百分比</label>  
                    <input type="number" v-model="formatBuff.scale" class="mui-input-clear" min="1" max="999" step="1" placeholder="取值1到999">  
                </div>  
                <div class="mui-input-row" v-show="showHeight">  
                    <label>高度</label>  
                    <input type="number" v-model="formatBuff.height" class="mui-input-clear" placeholder="高度,0表示根据宽度等比例缩放">  
                </div>  
                <div class="mui-input-row" v-show="showWidth">  
                    <label>宽度</label>  
                    <input type="number" v-model="formatBuff.width" class="mui-input-clear" placeholder="宽度,0表示根据高度等比例缩放">  
                </div>  
                <div class="mui-input-row" v-show="showArea">  
                    <label>像素限制</label>  
                    <input type="number" v-model="formatBuff.area" class="mui-input-clear" placeholder="只对jpg有效">  
                </div>  

                <div class="mui-input-row" v-show="showInput">  
                    <label>图片质量</label>  
                    <input type="number" v-model="formatBuff.quality" class="mui-input-clear" min="1" max="100" placeholder="从1到100,默认75">  
                </div>  

                <div class="mui-input-row" v-show="showInput">  
                    <label>输出文件格式</label>  
                    <input type="text" class="mui-input-clear" v-model="formatBuff.suffix" placeholder="默认为原图格式,如jpg,非法格式可能导致出错">  
                </div>  
                <div class="mui-input-row" v-show="showInput">  
                    <label>格式说明</label>  
                    <input type="text" class="mui-input-clear" v-model="formatBuff.desc" placeholder="格式说明,用途说明等,将作为该格式的备注显示">  
                </div>  
                <div class="mui-button-row">  
                    <button type="button" class="mui-btn mui-btn-link " v-show="showInput&&isEdit" @tap="submitNewFormat">放弃修改</button>  
                    <button type="button" class="mui-btn mui-btn-blue " v-show="showInput" @tap="submitNewFormat">{{saveBtnText}}</button>  

                </div>  
            </form>  

        </div>  

        <div class="mui-text-center" style="margin-top: 30px; bottom: 0px;width: 100%;">  
            © 2017 -  
            <a l-href="https://www.betweenfriends.cn"> 友间共享 </a>  
            <a l-href="https://blog.betweenfriends.cn/about"> 关于我们 </a> - 闽ICP备17012098号-1  
        </div>  
    </body>  

</html>
代码解读
  1. 利用mui控制UI的布局和渲染展示
  2. vue负责绑定ui和后台数据,并跟踪数据变化
  3. Page是我自己定义的框架,源码就不方便分享了,简单的实现参考怎么实现一站式跨平台开发

原文地址:https://blog.betweenfriends.cn/post/crossdomaindev.html

继续阅读 »

前言

这只是个人的经验总结和分享,并不是,也没时间做一个完整的教程。
分享的是我在实际项目开发中碰到的杂七杂八的问题,仅供参考。

一个简单的图片压缩工具

在线地址: 图片压缩工具-友间共享

在工作过程中,难免会碰到需要为同一个图片生成多种分辨率的情形,比如打包app之前需要准备好的各类图标,虽然简单,但是费点时间在所难免,就自己做了一个小工具,主要功能如下:

  1. 可以自定义处理后的图片的分辨率和压缩方式等,自定义的样式自动保存,下次可以重复使用。
  2. 一键上传图片,根据预设的样式批量生成处理结果,根据需要再自行下载

alt

开发环境

  1. vue+mui+webpack+npm+macos
  2. 后台服务: 七牛云存储

运行环境

在线服务,可在支持h5的浏览器上运行,电脑,手机平板理论上都可以

在线地址: 图片压缩工具-友间共享

实现原理

其实很简单,就是先上传图片到七牛云服务
然后在页面上提供配置页面,保存用户配置的格式,然后把每个格式都转化成七牛的图片转化服务的格式化字符串,最后把这个字符串和图片的原地址合并成图片下载链接,就可以在点击链接时调用七牛的图片处理服务,按照我们设定的格式下载转化后的图片。

项目目录结构

alt

** 详细目录结构介绍和开发环境部署参考 怎么搭建vue和Mui的多页面开发环境

主要代码

引入vue,mui等库
import LContext from 'lui/context.js'  
import Vue from 'vue'  
import 'babel-polyfill'  
import Vuex from 'vuex'  
import $ from './mui/mui.min'  

require('../css/mui.min.css')  
require('../css/icons-extra.css')  
//require('../css/iconfont.css')  
//require('../css/app.css')  
require('../css/app.less')  
window.Vue = Vue  
window.Vuex = Vuex  
window.$ = $  
window.mui = $  

require('./mui/mui.pullToRefresh')  
require('./mui/mui.pullToRefresh.material')  

//加载BASE64  
require('exports-loader!./util/base64.js')  
Vue.use(Vuex)  

//import LoginDialog from 'app/Login'  

if(process.env.NODE_ENV === 'production') {  
    Vue.config.productionTip = false  
}

有点偷懒,部分变量其实是可以在环境配置时引入的,这里就先忽略了,反正效果也一样

其中的库可根据需要引入,因为是从我自己的架构上COPY下来的,有些文件和库在这个项目中是可以不用引入的。引用的路径和webpack环境配置有关,以下是我的配置

    resolve: {  
        extensions: ['.js', '.vue', '.json'],  
        alias: { //定义别名,简易方式,可以在require调用时缩短路径  
            'vue$': 'vue/dist/vue.esm.js',  
            '@': resolve('src'),  
            "page$": resolve('src/js/page.js'),  
            'app': resolve('src/components/'),  
            'lui': resolve('src/js/lui/'),  
            'mui': resolve('src/js/mui/'),  
            'util': resolve('src/js/util/')  
        }  
    },
output.js代码
import Page from 'page'  

import Uploader from 'lui/upload.js'  

var formVue = new Vue({  
    el: '.mui-content',  
    data: {  
        src: '',  
        formats: [],  
        formatBuff: {  
            facs: 9,  
            scaleType: '按指定宽高值强行缩略,可能导致目标图片变形,width和height取值范围1-9999'  
        }, //当前正在编辑的图片类型  
        collapsePanels: {  
            scale: false  
        },  
        logined: false  
    },  
    computed: {  
        showScale: function() {  
            switch(this.formatBuff.facs) {  
                case 1:  
                    return true;  
                case 2:  
                    return true;  
                case 3:  
                    return true;  
                default:  
                    break;  
            }  
            return false;  
        },  
        showHeight: function() {  
            switch(this.formatBuff.facs) {  
                case 5:  
                    return true;  

                case 7:  
                    return true;  
                case 8:  
                    return true;  

                case 9:  
                    return true;  

                case 10:  
                    return true;  

                case 11:  
                    return true;  
                default:  
                    break;  
            }  
            return false;  
        },  
        showWidth: function() {  
            switch(this.formatBuff.facs) {  
                case 4:  
                    return true;  
                case 7:  
                    return true;  
                case 8:  
                    return true;  

                case 9:  
                    return true;  
                case 10:  
                    return true;  

                case 11:  
                    return true;  
                default:  
                    break;  
            }  
            return false  
        },  
        showArea: function() {  
            switch(this.formatBuff.facs) {  
                case 12:  
                    return true;  
                default:  
                    break;  
            }  
            return false;  
        },  
        showInput: function() {  
            if(this.formatBuff.facs > 0) {  
                return true  
            }  
            return false  
        },  
        srcUri: function() {  
            if(this.src) {  
                return LContext.fileServer + this.src + LContext.image.Thumbnail  
            }  
            return ''  
        },  
        srcThumbnail: function() {  
            if(this.src) {  
                return LContext.fileServer + this.src +  
                    LContext.image.Thumbnail  
            }  
            return ''  
        },  
        isEdit: function() {  
            if(this.formatBuff.uid) {  
                return true  
            }  
            return false  
        },  
        saveBtnText: function() {  
            console.log(this.isEdit + '  edit')  
            if(this.isEdit) {  
                return '保存修改'  
            } else {  
                return '保存新建样式'  
            }  
        }  
    },  
    methods: {  
        downLoad: function(format) {  
            var _format = this.decodeFormat(format)  
            if(_format.length > 2) {  
                lpage.open(LContext.fileServer + this.src + '?imageMogr2' + _format, undefined, 'new')  
            } else {  
                lpage.alert('下载格式无效!')  
            }  
        },  
        downUrl: function(format) {  
            var _format = this.decodeFormat(format)  
            if(_format.length > 2) {  
                return LContext.fileServer + this.src + '?imageMogr2' + _format  
            } else {  
                return false  
            }  
        },  
        deleteFormat: function(format) {  
            for(var i = 0; i < this.formats.length; i++) {  
                if(this.formats[i].uid === format.uid) {  
                    this.formats.splice(i, 1)  
                    lpage.saveFormats()  
                }  
            }  
        },  
        setFacs: function(facs, event) {  
            //          this.formatBuff.facs = parseInt(facs, 10)  
            Vue.set(this.formatBuff, 'facs', parseInt(facs, 10))  
            Vue.set(this.formatBuff, 'scaleType', event.currentTarget.innerText)  

            Vue.nextTick(function() {  
                this.closePanel('scale')  
            }.bind(this))  
            //          console.log(JSON.stringify(this.formatBuff))  
        },  
        decodeFormat: function(format) {  
            //缩放  
            var formatStr = '/thumbnail/'  
            switch(format.facs) {  
                case 1:  
                    formatStr += '!' + format.scale + 'p'  
                    break;  
                case 2:  
                    formatStr += '!' + format.scale + 'px'  
                    break;  
                case 3:  
                    formatStr += '!x' + format.scale + 'p'  
                    break;  
                case 4:  
                    formatStr += format.width + 'x'  
                    break;  
                case 5:  
                    formatStr += 'x' + format.height  
                    break;  

                case 7:  
                    formatStr += format.width + 'x' + format.height  
                    break;  

                case 8:  
                    formatStr += '!' + format.width + 'x' + format.height  
                    break;  

                case 9:  
                    formatStr += format.width + 'x' + format.height + '!'  
                    break;  

                case 10:  
                    formatStr += format.width + 'x' + format.height + '>'  
                    break;  

                case 11:  
                    formatStr += format.width + 'x' + format.height + '<'  
                    break;  
                case 12:  
                    formatStr += format.area + '@'  
                    break;  
                default:  
                    formatStr = ''  
                    break;  
            }  
            //格式转换  
            if(format.suffix !== 'auto') {  
                formatStr += '/format/' + format.suffix  
            }  
            return formatStr  
        },  
        isActiveFacs: function(facs) {  
            if(this.formatBuff.facs === facs) {  
                return true  
            }  
            return false  
        },  
        panelActive: function(collapseList) {  
            return this.collapsePanels[collapseList]  
        },  
        togglePanel: function(collapseList) {  
            Vue.set(this.collapsePanels, collapseList, !this.collapsePanels[collapseList])  
        },  
        closePanel: function(collapseList) {  
            Vue.set(this.collapsePanels, collapseList, false)  
        },  
        submitNewFormat: function() {  
            var newFormat = {  
                uid: 'format_' + new Date().getTime(),  
                height: this.formatBuff.height,  
                width: this.formatBuff.width,  
                scale: this.formatBuff.scale,  
                area: this.formatBuff.area,  
                facs: this.formatBuff.facs,  
                desc: this.formatBuff.desc,  
                scaleType: this.formatBuff.scaleType,  
                suffix: this.formatBuff.suffix ? this.formatBuff.suffix : 'auto', //图片类型  
                quality: this.formatBuff.quality ? this.formatBuff.quality : 75 //图片质量  
            }  
            var change = false  
            if(this.formatBuff.uid) {  
                for(var i = 0; i < this.formats.length; i++) {  
                    if(this.formats[i].uid === this.formatBuff.uid) {  
                        newFormat.uid = this.formatBuff.uid  
                        this.formatBuff.uid = undefined  
                        Vue.set(this.formats, i, newFormat)  
                        change = true  
                    }  
                }  
            }!change && this.formats.push(newFormat)  
            lpage.saveFormats()  
        },  
        editFormat: function(format) {  
            Vue.set(this.formatBuff, 'uid', format.uid)  
            Vue.set(this.formatBuff, 'height', format.height)  
            Vue.set(this.formatBuff, 'width', format.width)  
            Vue.set(this.formatBuff, 'scale', format.scale)  
            Vue.set(this.formatBuff, 'area', format.area)  
            Vue.set(this.formatBuff, 'facs', format.facs)  
            Vue.set(this.formatBuff, 'desc', format.desc)  
            Vue.set(this.formatBuff, 'scaleType', format.scaleType)  
            Vue.set(this.formatBuff, 'suffix', format.suffix === 'auto' ? undefined : format.suffix)  
            Vue.set(this.formatBuff, 'quality', format.quality ? format.quality : 75)  
        }  
    }  
})  

var pcPage = {  
    initUI: function() {  
        this.initUploader()  
    },  
    initData: function() {  
        //      this.user.isLogin(function(logined) {  
        //          if(logined) {  
        //              formVue.logined = true  
        lpage.getFormats()  
        //          } else {  
        //              formVue.logined = false  
        //              lpage.user.login()  
        //          }  
        //      })  
    },  
    initUploader: function() {  
        new Uploader({  
            selector: 'image_uploader',  
            token: {  
                mode: 'imagescale',  
                image: { //图片  
                    prefix: '/utils/imagescale/'  
                }  
            },  
            success: function(data) {  
                formVue.src = data.key  
                lpage.saveFormats()  
            }  
        }).init()  
    },  
    saveFormats: function() {  
        lpage.setItem('_myFormats', JSON.stringify(formVue.formats))  
        lpage.setItem('_lastSrc', formVue.src)  
    },  
    getFormats: function() {  
        var _fmt = lpage.getItem('_myFormats')  
        if(!_fmt) {  
            _fmt = [{  
                'uid': 'format_1504082048978',  
                'height': '2208',  
                'width': '1242',  
                'scale': '50',  
                'facs': 9,  
                'desc': '苹果IphoneApp审核用的截屏图片1242*2208',  
                'scaleType': '按指定宽高值强行缩略,可能导致目标图片变形,width和height取值范围1-9999\n',  
                'suffix': 'auto',  
                'quality': '100'  
            }, {  
                'uid': 'format_1504144877541',  
                'height': '2732',  
                'width': '2048',  
                'scale': '50',  
                'facs': 9,  
                'desc': '苹果IpadApp审核用的截屏图片2048*2732',  
                'scaleType': '按指定宽高值强行缩略,可能导致目标图片变形,width和height取值范围1-9999\n',  
                'suffix': 'auto',  
                'quality': '100'  
            }]  
            Vue.set(formVue, 'formats', _fmt)  
        } else  

        if(_fmt) {  
            try {  
                _fmt = JSON.parse(_fmt)  
                Vue.set(formVue, 'formats', _fmt)  
            } catch(e) {  
                //TODO handle the exception  
                console.log('err')  
            }  
        }  

        _fmt = lpage.getItem('_lastSrc')  
        if(_fmt) {  
            formVue.src = _fmt  
        }  
    }  
}  

Page.init({  
    pc: pcPage  
})
output.html源码
<!DOCTYPE html>  
<html class="feedback">  

    <head>  
        <meta charset="utf-8">  
        <meta name="viewport" content="width=device-width, initial-scale=1,maximum-scale=1,user-scalable=no">  
        <meta name="apple-mobile-web-app-capable" content="yes">  
        <meta name="apple-mobile-web-app-status-bar-style" content="black">  
        <title>友间共享-图片处理</title>  
        <style>  
            .mui-content .mui-table-view-radio .mui-table-view-cell>a:not(.mui-btn) {  
                text-align: left;  
                white-space: normal;  
            }  
        </style>  
    </head>  

    <body>  
        <header class="mui-bar mui-bar-nav">  
            <a class="mui-action-back mui-btn  mui-btn-link mui-btn-nav mui-pull-left"><span class=" mui-icon mui-icon-left-nav "></span> </a>  
            <h1 class="mui-title">友间共享-图片处理</h1>  
        </header>  

        <div class="mui-content">  

            <p class="mui-content-padded">可将上传的图片一次性转化输出成多个指定高度/宽度的图片,每次上传文件会扣除一定数额的积分,积分可通过多种方式获取  
                <a l-href="https://www.betweenfriends.cn/view/mine/bonusrule.html">积分获取/使用规则</a>  
            </p>  
            <div class="mui-content-padded ">  
                <img :src="srcUri" style="max-width: 250px;" />  
                <div class="image-list">  
                    <div class="image-item space">  
                        <button type="button" class="mui-btn mui-btn-blue">点击上传</button>  
                        <div class="file">  
                            <form id="image_uploader"></form>  
                        </div>  
                    </div>  
                </div>  
            </div>  

            <p class="mui-content-padded">转换后的图片下载</p>  
            <ul class="mui-table-view">  
                <li v-for="format in formats" class="mui-table-view-cell mui-media">  
                    <img class="mui-media-object mui-pull-left" :src="srcThumbnail">  
                    <div class="mui-media-body">  
                        <p>{{format.desc}}</p>  
                        <button type="button" class="mui-btn mui-btn-link" @tap="deleteFormat(format)">删除样式</button>  
                        <button type="button" class="mui-btn mui-btn-link" @tap="editFormat(format)">编辑样式</button>  
                        <a class="mui-btn mui-btn-blue" :href="downUrl(format)" download="格式化图片">下载图片</a>  
                    </div>  
                </li>  
            </ul>  

            <hr />  
            <p class="mui-content-padded" style="margin-top: 20px;">图片处理参数配置,可在此处新增图片样式</p>  
            <ul class="mui-table-view">  

                <li class="mui-table-view-cell mui-collapse" :class="{'mui-active':panelActive('scale')}" @tap="togglePanel('scale')">  
                    <a class="mui-navigate-right">  
                        缩放模式</a>  
                    <ul class="mui-table-view mui-table-view-radio">  

                        <li class="mui-table-view-cell" :class="{'mui-selected':isActiveFacs(9)}" @tap="setFacs(9,$event)">  
                            <a class="mui-navigate-right">  
                                按指定宽高值强行缩略,可能导致目标图片变形,width和height取值范围1-9999  
                            </a>  
                        </li>  

                        <li class="mui-table-view-cell" :class="{'mui-selected':isActiveFacs(1)}" @tap="setFacs(1,$event)">  
                            <a class="mui-navigate-right">  
                                基于原图大小,按指定百分比缩放。Scale取值范围1-999  
                            </a>  
                        </li>  
                        <li class="mui-table-view-cell" :class="{'mui-selected':isActiveFacs(2)}" @tap="setFacs(2,$event)">  
                            <a class="mui-navigate-right">  
                                以百分比形式指定目标图片宽度,高度不变。Scale取值范围1-999  
                            </a>  
                        </li>  
                        <li class="mui-table-view-cell" :class="{'mui-selected':isActiveFacs(3)}" @tap="setFacs(3,$event)">  
                            <a class="mui-navigate-right">  
                                以百分比形式指定目标图片高度,宽度不变。Scale取值范围1-999  
                            </a>  
                        </li>  
                        <li class="mui-table-view-cell" :class="{'mui-selected':isActiveFacs(4)}" @tap="setFacs(4,$event)">  
                            <a class="mui-navigate-right">  
                                指定目标图片宽度,高度等比缩放,Width取值范围1-9999  
                            </a>  
                        </li>  
                        <li class="mui-table-view-cell" :class="{'mui-selected':isActiveFacs(5)}" @tap="setFacs(5,$event)">  
                            <a class="mui-navigate-right">  
                                指定目标图片高度,宽度等比缩放,Height取值范围1-9999  
                            </a>  
                        </li>  

                        <li class="mui-table-view-cell" :class="{'mui-selected':isActiveFacs(7)}" @tap="setFacs(7,$event)">  
                            <a class="mui-navigate-right">  
                                等比缩放,比例值为宽缩放比和高缩放比的较小值,Width 和 Height 取值范围1-9999。 注意:宽缩放比:目标宽/原图宽   高缩放比:目标高/原图高  
                            </a>  
                        </li>  
                        <li class="mui-table-view-cell" :class="{'mui-selected':isActiveFacs(8)}" @tap="setFacs(8,$event)">  
                            <a class="mui-navigate-right">  
                                等比缩放,比例值为宽缩放比和高缩放比的较大值,Width 和 Height 取值范围1-9999。 注意:宽缩放比:目标宽/原图宽   高缩放比:目标高/原图高  
                            </a>  
                        </li>  

                        <li class="mui-table-view-cell" :class="{'mui-selected':isActiveFacs(10)}" @tap="setFacs(10,$event)">  
                            <a class="mui-navigate-right">  
                                等比缩小,比例值为宽缩放比和高缩放比的较小值。如果目标宽和高都大于原图宽和高,则不变,Width 和 Height 取值范围1-9999。 注意:宽缩放比:目标宽/原图宽   高缩放比:目标高/原图高;  
                            </a>  
                        </li>  

                        <li class="mui-table-view-cell" :class="{'mui-selected':isActiveFacs(11)}" @tap="setFacs(11,$event)">  
                            <a class="mui-navigate-right">  
                                等比放大,比例值为宽缩放比和高缩放比的较小值。如果目标宽(高)小于原图宽(高),则不变,Width 和 Height 取值范围1-9999。 注意: 宽缩放比:目标宽/原图宽   高缩放比:目标高/原图高  
                            </a>  
                        </li>  

                        <li class="mui-table-view-cell" :class="{'mui-selected':isActiveFacs(12)}" @tap="setFacs(12,$event)">  
                            <a class="mui-navigate-right">  
                                按原图高宽比例等比缩放,缩放后的像素数量不超过指定值,Area取值范围1-24999999  
                            </a>  
                        </li>  
                    </ul>  
                </li>  
            </ul>  
            <p class="mui-content-padded" v-show="showInput">{{formatBuff.scaleType}}</p>  
            <form class="mui-input-group">  
                <div class="mui-input-row" v-show="showScale">  
                    <label>缩放百分比</label>  
                    <input type="number" v-model="formatBuff.scale" class="mui-input-clear" min="1" max="999" step="1" placeholder="取值1到999">  
                </div>  
                <div class="mui-input-row" v-show="showHeight">  
                    <label>高度</label>  
                    <input type="number" v-model="formatBuff.height" class="mui-input-clear" placeholder="高度,0表示根据宽度等比例缩放">  
                </div>  
                <div class="mui-input-row" v-show="showWidth">  
                    <label>宽度</label>  
                    <input type="number" v-model="formatBuff.width" class="mui-input-clear" placeholder="宽度,0表示根据高度等比例缩放">  
                </div>  
                <div class="mui-input-row" v-show="showArea">  
                    <label>像素限制</label>  
                    <input type="number" v-model="formatBuff.area" class="mui-input-clear" placeholder="只对jpg有效">  
                </div>  

                <div class="mui-input-row" v-show="showInput">  
                    <label>图片质量</label>  
                    <input type="number" v-model="formatBuff.quality" class="mui-input-clear" min="1" max="100" placeholder="从1到100,默认75">  
                </div>  

                <div class="mui-input-row" v-show="showInput">  
                    <label>输出文件格式</label>  
                    <input type="text" class="mui-input-clear" v-model="formatBuff.suffix" placeholder="默认为原图格式,如jpg,非法格式可能导致出错">  
                </div>  
                <div class="mui-input-row" v-show="showInput">  
                    <label>格式说明</label>  
                    <input type="text" class="mui-input-clear" v-model="formatBuff.desc" placeholder="格式说明,用途说明等,将作为该格式的备注显示">  
                </div>  
                <div class="mui-button-row">  
                    <button type="button" class="mui-btn mui-btn-link " v-show="showInput&&isEdit" @tap="submitNewFormat">放弃修改</button>  
                    <button type="button" class="mui-btn mui-btn-blue " v-show="showInput" @tap="submitNewFormat">{{saveBtnText}}</button>  

                </div>  
            </form>  

        </div>  

        <div class="mui-text-center" style="margin-top: 30px; bottom: 0px;width: 100%;">  
            © 2017 -  
            <a l-href="https://www.betweenfriends.cn"> 友间共享 </a>  
            <a l-href="https://blog.betweenfriends.cn/about"> 关于我们 </a> - 闽ICP备17012098号-1  
        </div>  
    </body>  

</html>
代码解读
  1. 利用mui控制UI的布局和渲染展示
  2. vue负责绑定ui和后台数据,并跟踪数据变化
  3. Page是我自己定义的框架,源码就不方便分享了,简单的实现参考怎么实现一站式跨平台开发

原文地址:https://blog.betweenfriends.cn/post/crossdomaindev.html

收起阅读 »

分享自动生成ios离线打包所需各种大小icon的gulp代码

图标 离线打包 App离线打包

离线打包时需要各种大小分辨率不同的启动图标,手动一个个制作太麻烦了。
经过我半天的研究,终于写出了可以自动生成各种所需启动图标的gulp代码,如下:

var gulp = require('gulp')  
var imageResize = require('gulp-image-resize')  
var rename = require('gulp-rename')  

gulp.task('icon-ios', () => {  
    var sizes = [29, 40, 50, 57, 58, 72, 76, 80, 87, 100, 114, 120, 144, 152, 180]  
    sizes.forEach((size, index) => {  
        gulp.src('icon.png')  
            .pipe(imageResize({  
                width: size,  
                height: size,  
                corp: true  
            }))  
            .pipe(rename('icon'+size+'.png'))  
            .pipe(gulp.dest('dist'))  
    })  
})

需要注意的是gulp-image-resize是需要额外安装两个依赖的,可以去官网查看:
gulp-image-resize

希望官方能把这段经验加到离线打包的文档里,防止后人走弯路

继续阅读 »

离线打包时需要各种大小分辨率不同的启动图标,手动一个个制作太麻烦了。
经过我半天的研究,终于写出了可以自动生成各种所需启动图标的gulp代码,如下:

var gulp = require('gulp')  
var imageResize = require('gulp-image-resize')  
var rename = require('gulp-rename')  

gulp.task('icon-ios', () => {  
    var sizes = [29, 40, 50, 57, 58, 72, 76, 80, 87, 100, 114, 120, 144, 152, 180]  
    sizes.forEach((size, index) => {  
        gulp.src('icon.png')  
            .pipe(imageResize({  
                width: size,  
                height: size,  
                corp: true  
            }))  
            .pipe(rename('icon'+size+'.png'))  
            .pipe(gulp.dest('dist'))  
    })  
})

需要注意的是gulp-image-resize是需要额外安装两个依赖的,可以去官网查看:
gulp-image-resize

希望官方能把这段经验加到离线打包的文档里,防止后人走弯路

收起阅读 »

怎么搭建vue和Mui的多页面开发环境

打包 Vue

现在有很多的打包工具可以选择,grunt,gulp,webpack等等。vue官方建议是使用webpack,而且webpack相对其他打包工具的好处是代码动态编译,可以根据需要提取出公共部分的代码。因此我们也选择webpack作为打包工具。

目录结构

alt

开发目录 src
  1. assets 静态资源,如图片等的保存
  2. css 样式文件,css,less等
  3. fonts 字体文件,根据需要添加自定义字体
  4. js js框架,公共部分代码,自定义组件等
    5.view html页面文件,页面入口js文件
build 打包参数配置

主要用于对打包过程的控制

  1. webpack.base.conf.js 基础配置参数,开发和生成代码打包共用
  2. webpack.dev.conf.js 开发环境下的打包参数配置
  3. webpack.prod.conf.js 生产环境的打包参数配置
config 打包模式配置

主要是打包相关的基础参数配置,如文件存放目录,打包后的访问根目录,是否压缩等

assets 生产环境下的js/css/图片等文件保存路径

可在config中指定

view 生产环境下的页面文件Html保存路径

和src/view下的html文件一一对应

package.json 打包的环境依赖配置

使用方式

  1. 开发 npm run dev 开启本地web服务,方便调试页面和代码

  2. 生产环境打包 npm run build 将src下的代码打包到根目录下的 assets和view目录下

功能

  1. 支持vue开发的调试,支持webpack打包的调试和自动更新,不需要手动刷新代码

  2. 支持代码和语法检查,可在根目录的.eslintrc下配置检查规则

3.支持webpack的多页面(多入口)开发,相比单入口模式,逻辑更清楚

使用说明

1.view下的页面必须放在view的下级目录,比如view/index/index.html,同时每个页面文件必须搭配一个同名的js文件作为页面入口,比如view/index/index.js

2.可根据需要配置本地调试服务器的端口,默认8000,
在config/index下配置

3.可配置打包路径等,同样在config/index下配置

初始化说明

  1. 在根目录下执行 npm install (如果不是root用户的话,sudo npm install)

  2. 打包或者调试时,如果npm 命令提示错误,一般是某个模块未安装,可执行 npm install ***(模块名称) 安装相关模块

  3. npm run dev (调试)

  4. npm run build (正式打包)

下载完整目录

原文链接:https://blog.betweenfriends.cn/post/howtodevwithvueandmui.html

继续阅读 »

现在有很多的打包工具可以选择,grunt,gulp,webpack等等。vue官方建议是使用webpack,而且webpack相对其他打包工具的好处是代码动态编译,可以根据需要提取出公共部分的代码。因此我们也选择webpack作为打包工具。

目录结构

alt

开发目录 src
  1. assets 静态资源,如图片等的保存
  2. css 样式文件,css,less等
  3. fonts 字体文件,根据需要添加自定义字体
  4. js js框架,公共部分代码,自定义组件等
    5.view html页面文件,页面入口js文件
build 打包参数配置

主要用于对打包过程的控制

  1. webpack.base.conf.js 基础配置参数,开发和生成代码打包共用
  2. webpack.dev.conf.js 开发环境下的打包参数配置
  3. webpack.prod.conf.js 生产环境的打包参数配置
config 打包模式配置

主要是打包相关的基础参数配置,如文件存放目录,打包后的访问根目录,是否压缩等

assets 生产环境下的js/css/图片等文件保存路径

可在config中指定

view 生产环境下的页面文件Html保存路径

和src/view下的html文件一一对应

package.json 打包的环境依赖配置

使用方式

  1. 开发 npm run dev 开启本地web服务,方便调试页面和代码

  2. 生产环境打包 npm run build 将src下的代码打包到根目录下的 assets和view目录下

功能

  1. 支持vue开发的调试,支持webpack打包的调试和自动更新,不需要手动刷新代码

  2. 支持代码和语法检查,可在根目录的.eslintrc下配置检查规则

3.支持webpack的多页面(多入口)开发,相比单入口模式,逻辑更清楚

使用说明

1.view下的页面必须放在view的下级目录,比如view/index/index.html,同时每个页面文件必须搭配一个同名的js文件作为页面入口,比如view/index/index.js

2.可根据需要配置本地调试服务器的端口,默认8000,
在config/index下配置

3.可配置打包路径等,同样在config/index下配置

初始化说明

  1. 在根目录下执行 npm install (如果不是root用户的话,sudo npm install)

  2. 打包或者调试时,如果npm 命令提示错误,一般是某个模块未安装,可执行 npm install ***(模块名称) 安装相关模块

  3. npm run dev (调试)

  4. npm run build (正式打包)

下载完整目录

原文链接:https://blog.betweenfriends.cn/post/howtodevwithvueandmui.html

收起阅读 »

布局理念 - NView模板 - wap2app教程

NView模板

布局容器

在NView模板中,<nview>标签是布局容器,其它控件都必须包裹在<nview>容器中;<nview>默认为块级元素(宽度为100%屏幕宽度),自上而下排列布局,现阶段必须声明nview的高度,不支持高度自适应。如下是一个示例

    <nviews cachemaxage="86400">  
        <nview id="nview1" style="height:50px;backgroundColor:#56C1FF"></nview>  
        <nview id="nview2" style="height:100px;backgroundColor:#61D836"></nview>  
        <nview id="nview3" style="height:50px;backgroundColor:#EF5FA7"></nview>  
    </nviews>

如上代码布局效果如下(顶部自带原生标题栏):

除了默认的自上而下顺序布局,nview也支持绝对定位布局,如下示例中,将nview1设置为绝对定位,在窗口底部显示;

    <nviews cachemaxage ="86400">  
        <nview id="nview1" style="position:absolute;bottom:0;height:50px;backgroundColor:#56C1FF"></nview>  
        <nview id="nview2" style="height:100px;backgroundColor:#61D836"></nview>  
        <nview id="nview3" style="height:50px;backgroundColor:#EF5FA7"></nview>  
    </nviews>

设置nview1为绝对定位后,运行示意图如下:

从运行结果可以看出,nview1设置为绝对定位后,脱离文档流自行定位;但nview2、nview3继续使用默认的自上而下的布局方式。

在nview布局容器下,可以嵌套imageslider、list、canvas、richtext四个子标签,子标签默认和nview容器具备相同的宽高;其中:

  • imageslider/list是封装好的组件,标签及布局方式固定;
  • canvas/richtext是布局控件,分别表示绝对布局/流式布局

接下来重点介绍 richtext/canvas两种布局控件

布局控件

先看如下代码,有两个nview,分别嵌套流式布局的richtext和绝对布局的canvas:

    <nviews cachemaxage ="86400">  
        <nview id="nview_richtext" style="height:100px;backgroundColor:#56C1FF">  
            <richtext>  
                <font>Hello</font>  
                <font>World</font>  
            </richtext>  
        </nview>  
        <nview id="nview_canvas" style="height:100px;backgroundColor:#EF5FA7">  
            <canvas>  
                <font>Hello</font>  
                <font>World</font>  
            </canvas>  
        </nview>  
    </nviews>

运行结果如下:

从图中可以,richtext下的两个font流式排版,显示正常;但canvas下的两个font重叠在一起,这是因为canvas下的子标签默认为绝对定位,不进入文档流,多个子标签则会以多层的方式重叠排版;给canvas下子标签设置位置坐标即可正常布局,代码如下:

<nviews cachemaxage ="86400">  
    <nview id="nview_richtext" style="height:100px;backgroundColor:#56C1FF">  
        <richtext>  
            <font>Hello</font>  
            <font>World</font>  
        </richtext>  
    </nview>  
    <nview id="nview_canvas" style="height:100px;backgroundColor:#EF5FA7">  
        <canvas>  
            <!--设置坐标位置-->  
            <font style="left:5px;width:40px;">Hello</font>  
            <font style="left:45px;width:40px;">World</font>  
        </canvas>  
    </nview>  
</nviews>

对应运行结果如下:

canvas绝对布局

canvas是绝对布局的容器标签,具有以下特点:

  • 支持a、font、img、button、input、hr子标签
  • 子标签全部绝对定位,需要声明标签相对父元素的相对坐标值
  • 子标签大小由坐标固定,不会随着内容自动变化,若内容过长则自动裁剪(超出部分不可见)

如若nview标签下仅有一个canvas子标签,则canvas和nview默认具有相同的的大小/宽高信息。 如下是一个代码示例,演示canvas子标签如何定位:

    <nviews cachemaxage ="86400">  
        <nview id="nview1" style="height:150px;background-color: #56C1FF;">  
            <canvas>  
                <font style="left:35px;top:35px;height:22px;align:left;font-size:17px;color:#ffffff">  
                    这里是文字  
                </font>  
                <img src="_www/logo.png" style="top:60px;right:60px;width:60px;height:60px"/>  
            </canvas>  
        </nview>  
        <nview id="nview2" style="height:150px;background-color: #EF5FA7;">  
            <canvas>  
                <button style="left:50px;right:50px;top:70px;height:30px;background-color:rgba(0,0,0,0);border-color:#ffffff;color:#ffffff;">按钮</button>  
            </canvas>  
        </nview>  
    </nviews>

运行结果如下(为方便理解,标注了每个元素的坐标值,坐标值对应上面代码中的left/right/top等定义):

从上图可以看出:canvas布局控件下的元素,全部为精确定位,坐标值是相对于父容器的坐标(不是屏幕坐标)。

每个元素均需声明位置坐标

canvas布局控件下的每个子标签均需要声明坐标位置,否则每个标签将作为单独一层重叠绘制,如下是一个示例,创建两个nview,显示相同的元素,第一个不设置坐标位置,第二个设置坐标位置,示例代码如下:

    <nviews cachemaxage ="86400">  
        <nview id="nview1" style="height:100px;background-color: #56C1FF;">  
            <canvas>  
                <!--未设置坐标位置-->  
                <font style="height:20px;align:left;color:#ffffff;font-size:17px;">作者:CHB</font>  
                <button style="width:50px;height:25px;background-color:#007aff;color:#ffffff;border-color:#007aff;">关注</button>  
            </canvas>  
        </nview>  
        <nview id="nview2" style="height:100px;background-color: #EF5FA7;">  
            <canvas>  
                <!--设置坐标位置-->  
                <font style="left:10px;top:10px;height:20px;align:left;color:#ffffff;font-size:17px;">作者:CHB</font>  
                <button style="left:100px;top:10px;width:50px;height:25px;background-color:#007aff;color:#ffffff;border-color:#007aff;">关注</button>  
            </canvas>  
        </nview>  
    </nviews>

运行结果如下:

因为每个元素均需要声明位置信息,因此canvas布局适合精确定位的场景,比如:居右显示;

逐层渲染,非流式

另一方面,canvas布局不适合可变长度的场景,如上示例中,若将"作者:CHB"替换为“作者:George R.R. Martin”,默认就会和右侧关注按钮重叠,代码如下:

    <nviews cachemaxage ="86400">  
        <nview id="nview1" style="height:100px;background-color: #56C1FF;">  
            <canvas>  
                <font style="left:10px;top:10px;height:20px;align:left;color:#ffffff;font-size:17px;">作者:CHB</font>  
                <button style="left:100px;top:10px;width:50px;height:25px;background-color:#007aff;color:#ffffff;border-color:#007aff;">关注</button>  
            </canvas>  
        </nview>  
        <nview id="nview2" style="height:100px;background-color: #EF5FA7;">  
            <canvas>  
                <!--名字较长时,会重叠绘制-->  
                <font style="left:10px;top:10px;height:20px;align:left;color:#ffffff;font-size:17px;">作者:George R.R. Martin</font>  
                <button style="left:100px;top:10px;width:50px;height:25px;background-color:#007aff;color:#ffffff;border-color:#007aff;">关注</button>  
            </canvas>  
        </nview>  
    </nviews>

运行结果如下:

从上图可看出,关注按钮重叠在"George R.R. Martin”名字上方,这有如下两个原因:

  • canvas下元素定位,类似于HTML5中的absolute定位,每个元素是按照坐标位置逐层绘制的,故关注按钮会和作者名字重叠,而不会像流式布局那样自动向右侧迁移;
  • canvas下font标签默认宽度为100%,故可以将"George R.R. Martin”完整显示出来,而没有出现裁剪隐藏的情况;

richtext流式布局

richtext是流式布局的容器标签,有以下特点:

  • 支持font、img、a、hr、br子标签;
  • 各个子标签自左向右、自上向下流式排版;
  • 子标签宽度和高度随内容自动变化,设置width/height属性无效(img标签例外),碰到较长文本时会自动换行;
  • 支持通过空格( )来设置元素前后间距
  • 设置位置坐标(left/top等)无效

如下是一个richtext示例:

    <nviews cacheMaxAge="86400">  
        <nview id="nview_richtext" style="height:250px;background-color: #56C1FF;">  
            <richtext style="left:10px;right:10px;width:340px;top:10px">  
                <font style="font-size:23px;color:#ffffff;font-weight: bold;">这是一级标题,内容较长时会自动换行</font>  
                <br/><br/>  
                <img src="_www/logo.png" width="20px" height="20px"></img>  
                <font style="font-size: 17px;color:#ffffff">&nbsp;&nbsp;&nbsp;&nbsp;作者:CHB&nbsp;&nbsp;&nbsp;&nbsp;</font>  
                <font style="font-size: 15px;color:#ffffff">2017-09-04</font>  
                <br/>  
                <hr style="border-color:#EF5FA7"/>  
                <br/>  
                <font style="font-size:14px;color:#ffffff">原文链接:</font><a style="font-size:14px;color:#ffffff">http://www.example.com</a>  
                <br/><br/>  
                <font style="font-size:14px;color:#ffffff">下面是详细内容,文字较长时会自动进行换行,可变长度的文字推荐使用richtext布局控件...</font>  
            </richtext>  
        </nview>  
    </nviews>

如上代码中,头像信息、作者信息、发布时间是顺序编写的,最终自动从左到右绘制在一行,运行结果如下:

因为设置位置坐标无效,故无法实现精确定位,比如要求发布时间居右显示,则在richtext中无法实现;

两种布局适用场景

通过如上分析,可以理解两种布局适用的场景不同:

  • 需要多个元素自左向右显示,元素长度不固定,则使用richtext
  • 文本长度不确定,需要自动换行,则使用richtext
  • 图文混排,则使用richtext
  • 需要精确定位(如右侧对齐),则使用canvas
继续阅读 »

布局容器

在NView模板中,<nview>标签是布局容器,其它控件都必须包裹在<nview>容器中;<nview>默认为块级元素(宽度为100%屏幕宽度),自上而下排列布局,现阶段必须声明nview的高度,不支持高度自适应。如下是一个示例

    <nviews cachemaxage="86400">  
        <nview id="nview1" style="height:50px;backgroundColor:#56C1FF"></nview>  
        <nview id="nview2" style="height:100px;backgroundColor:#61D836"></nview>  
        <nview id="nview3" style="height:50px;backgroundColor:#EF5FA7"></nview>  
    </nviews>

如上代码布局效果如下(顶部自带原生标题栏):

除了默认的自上而下顺序布局,nview也支持绝对定位布局,如下示例中,将nview1设置为绝对定位,在窗口底部显示;

    <nviews cachemaxage ="86400">  
        <nview id="nview1" style="position:absolute;bottom:0;height:50px;backgroundColor:#56C1FF"></nview>  
        <nview id="nview2" style="height:100px;backgroundColor:#61D836"></nview>  
        <nview id="nview3" style="height:50px;backgroundColor:#EF5FA7"></nview>  
    </nviews>

设置nview1为绝对定位后,运行示意图如下:

从运行结果可以看出,nview1设置为绝对定位后,脱离文档流自行定位;但nview2、nview3继续使用默认的自上而下的布局方式。

在nview布局容器下,可以嵌套imageslider、list、canvas、richtext四个子标签,子标签默认和nview容器具备相同的宽高;其中:

  • imageslider/list是封装好的组件,标签及布局方式固定;
  • canvas/richtext是布局控件,分别表示绝对布局/流式布局

接下来重点介绍 richtext/canvas两种布局控件

布局控件

先看如下代码,有两个nview,分别嵌套流式布局的richtext和绝对布局的canvas:

    <nviews cachemaxage ="86400">  
        <nview id="nview_richtext" style="height:100px;backgroundColor:#56C1FF">  
            <richtext>  
                <font>Hello</font>  
                <font>World</font>  
            </richtext>  
        </nview>  
        <nview id="nview_canvas" style="height:100px;backgroundColor:#EF5FA7">  
            <canvas>  
                <font>Hello</font>  
                <font>World</font>  
            </canvas>  
        </nview>  
    </nviews>

运行结果如下:

从图中可以,richtext下的两个font流式排版,显示正常;但canvas下的两个font重叠在一起,这是因为canvas下的子标签默认为绝对定位,不进入文档流,多个子标签则会以多层的方式重叠排版;给canvas下子标签设置位置坐标即可正常布局,代码如下:

<nviews cachemaxage ="86400">  
    <nview id="nview_richtext" style="height:100px;backgroundColor:#56C1FF">  
        <richtext>  
            <font>Hello</font>  
            <font>World</font>  
        </richtext>  
    </nview>  
    <nview id="nview_canvas" style="height:100px;backgroundColor:#EF5FA7">  
        <canvas>  
            <!--设置坐标位置-->  
            <font style="left:5px;width:40px;">Hello</font>  
            <font style="left:45px;width:40px;">World</font>  
        </canvas>  
    </nview>  
</nviews>

对应运行结果如下:

canvas绝对布局

canvas是绝对布局的容器标签,具有以下特点:

  • 支持a、font、img、button、input、hr子标签
  • 子标签全部绝对定位,需要声明标签相对父元素的相对坐标值
  • 子标签大小由坐标固定,不会随着内容自动变化,若内容过长则自动裁剪(超出部分不可见)

如若nview标签下仅有一个canvas子标签,则canvas和nview默认具有相同的的大小/宽高信息。 如下是一个代码示例,演示canvas子标签如何定位:

    <nviews cachemaxage ="86400">  
        <nview id="nview1" style="height:150px;background-color: #56C1FF;">  
            <canvas>  
                <font style="left:35px;top:35px;height:22px;align:left;font-size:17px;color:#ffffff">  
                    这里是文字  
                </font>  
                <img src="_www/logo.png" style="top:60px;right:60px;width:60px;height:60px"/>  
            </canvas>  
        </nview>  
        <nview id="nview2" style="height:150px;background-color: #EF5FA7;">  
            <canvas>  
                <button style="left:50px;right:50px;top:70px;height:30px;background-color:rgba(0,0,0,0);border-color:#ffffff;color:#ffffff;">按钮</button>  
            </canvas>  
        </nview>  
    </nviews>

运行结果如下(为方便理解,标注了每个元素的坐标值,坐标值对应上面代码中的left/right/top等定义):

从上图可以看出:canvas布局控件下的元素,全部为精确定位,坐标值是相对于父容器的坐标(不是屏幕坐标)。

每个元素均需声明位置坐标

canvas布局控件下的每个子标签均需要声明坐标位置,否则每个标签将作为单独一层重叠绘制,如下是一个示例,创建两个nview,显示相同的元素,第一个不设置坐标位置,第二个设置坐标位置,示例代码如下:

    <nviews cachemaxage ="86400">  
        <nview id="nview1" style="height:100px;background-color: #56C1FF;">  
            <canvas>  
                <!--未设置坐标位置-->  
                <font style="height:20px;align:left;color:#ffffff;font-size:17px;">作者:CHB</font>  
                <button style="width:50px;height:25px;background-color:#007aff;color:#ffffff;border-color:#007aff;">关注</button>  
            </canvas>  
        </nview>  
        <nview id="nview2" style="height:100px;background-color: #EF5FA7;">  
            <canvas>  
                <!--设置坐标位置-->  
                <font style="left:10px;top:10px;height:20px;align:left;color:#ffffff;font-size:17px;">作者:CHB</font>  
                <button style="left:100px;top:10px;width:50px;height:25px;background-color:#007aff;color:#ffffff;border-color:#007aff;">关注</button>  
            </canvas>  
        </nview>  
    </nviews>

运行结果如下:

因为每个元素均需要声明位置信息,因此canvas布局适合精确定位的场景,比如:居右显示;

逐层渲染,非流式

另一方面,canvas布局不适合可变长度的场景,如上示例中,若将"作者:CHB"替换为“作者:George R.R. Martin”,默认就会和右侧关注按钮重叠,代码如下:

    <nviews cachemaxage ="86400">  
        <nview id="nview1" style="height:100px;background-color: #56C1FF;">  
            <canvas>  
                <font style="left:10px;top:10px;height:20px;align:left;color:#ffffff;font-size:17px;">作者:CHB</font>  
                <button style="left:100px;top:10px;width:50px;height:25px;background-color:#007aff;color:#ffffff;border-color:#007aff;">关注</button>  
            </canvas>  
        </nview>  
        <nview id="nview2" style="height:100px;background-color: #EF5FA7;">  
            <canvas>  
                <!--名字较长时,会重叠绘制-->  
                <font style="left:10px;top:10px;height:20px;align:left;color:#ffffff;font-size:17px;">作者:George R.R. Martin</font>  
                <button style="left:100px;top:10px;width:50px;height:25px;background-color:#007aff;color:#ffffff;border-color:#007aff;">关注</button>  
            </canvas>  
        </nview>  
    </nviews>

运行结果如下:

从上图可看出,关注按钮重叠在"George R.R. Martin”名字上方,这有如下两个原因:

  • canvas下元素定位,类似于HTML5中的absolute定位,每个元素是按照坐标位置逐层绘制的,故关注按钮会和作者名字重叠,而不会像流式布局那样自动向右侧迁移;
  • canvas下font标签默认宽度为100%,故可以将"George R.R. Martin”完整显示出来,而没有出现裁剪隐藏的情况;

richtext流式布局

richtext是流式布局的容器标签,有以下特点:

  • 支持font、img、a、hr、br子标签;
  • 各个子标签自左向右、自上向下流式排版;
  • 子标签宽度和高度随内容自动变化,设置width/height属性无效(img标签例外),碰到较长文本时会自动换行;
  • 支持通过空格( )来设置元素前后间距
  • 设置位置坐标(left/top等)无效

如下是一个richtext示例:

    <nviews cacheMaxAge="86400">  
        <nview id="nview_richtext" style="height:250px;background-color: #56C1FF;">  
            <richtext style="left:10px;right:10px;width:340px;top:10px">  
                <font style="font-size:23px;color:#ffffff;font-weight: bold;">这是一级标题,内容较长时会自动换行</font>  
                <br/><br/>  
                <img src="_www/logo.png" width="20px" height="20px"></img>  
                <font style="font-size: 17px;color:#ffffff">&nbsp;&nbsp;&nbsp;&nbsp;作者:CHB&nbsp;&nbsp;&nbsp;&nbsp;</font>  
                <font style="font-size: 15px;color:#ffffff">2017-09-04</font>  
                <br/>  
                <hr style="border-color:#EF5FA7"/>  
                <br/>  
                <font style="font-size:14px;color:#ffffff">原文链接:</font><a style="font-size:14px;color:#ffffff">http://www.example.com</a>  
                <br/><br/>  
                <font style="font-size:14px;color:#ffffff">下面是详细内容,文字较长时会自动进行换行,可变长度的文字推荐使用richtext布局控件...</font>  
            </richtext>  
        </nview>  
    </nviews>

如上代码中,头像信息、作者信息、发布时间是顺序编写的,最终自动从左到右绘制在一行,运行结果如下:

因为设置位置坐标无效,故无法实现精确定位,比如要求发布时间居右显示,则在richtext中无法实现;

两种布局适用场景

通过如上分析,可以理解两种布局适用的场景不同:

  • 需要多个元素自左向右显示,元素长度不固定,则使用richtext
  • 文本长度不确定,需要自动换行,则使用richtext
  • 图文混排,则使用richtext
  • 需要精确定位(如右侧对齐),则使用canvas
收起阅读 »

mui两种预加载详解和爬坑

预加载

官方文档上的解释如下:
方式一:通过mui.init方法中的preloadPages参数进行配置.

mui.init({
preloadPages:[
{
url:prelaod-page-url,
id:preload-page-id,
styles:{},//窗口参数
extras:{},//自定义扩展参数
subpages:[{},{}]//预加载页面的子页面
}
],
preloadLimit:5//预加载窗口数量限制(一旦超出,先进先出)默认不限制
});

方式二:通过mui.preload方法预加载.

var page = mui.preload({
url:new-page-url,
id:new-page-id,//默认使用当前页面的url作为id
styles:{},//窗口参数
extras:{}//自定义扩展参数
});

方式一多页面加载问题如下


方式二preload需要放在plusready里面,解决如下

继续阅读 »

官方文档上的解释如下:
方式一:通过mui.init方法中的preloadPages参数进行配置.

mui.init({
preloadPages:[
{
url:prelaod-page-url,
id:preload-page-id,
styles:{},//窗口参数
extras:{},//自定义扩展参数
subpages:[{},{}]//预加载页面的子页面
}
],
preloadLimit:5//预加载窗口数量限制(一旦超出,先进先出)默认不限制
});

方式二:通过mui.preload方法预加载.

var page = mui.preload({
url:new-page-url,
id:new-page-id,//默认使用当前页面的url作为id
styles:{},//窗口参数
extras:{}//自定义扩展参数
});

方式一多页面加载问题如下


方式二preload需要放在plusready里面,解决如下

收起阅读 »

mui.init多页面写法


mui多页面预加载写法,原文档里面没有写法也没有实例,很蛋疼。


mui多页面预加载写法,原文档里面没有写法也没有实例,很蛋疼。

刚看到的一篇关于手机设置禁止横屏的解决方案,值得收藏!!!

解决横屏问题:
(转载)
真机运行时,manifest并不会实时生效,要打包才生效。
横屏有3个层级:

  1. 手机禁止横屏
  2. 手机允许横屏,但manifest禁止横屏
  3. 手机允许横屏、manifest允许横屏,但页面代码禁止横屏。

js里控制:
//仅支持横屏显示
plus.screen.lockOrientation("landscape-primary");
//仅支持横屏反方向显示
plus.screen.lockOrientation('landscape-secondary');
//仅支持竖屏显示
plus.screen.lockOrientation("portrait-primary");
//仅支持竖屏反方向显示
plus.screen.lockOrientation("portrait-secondary");

或(该方式未经测试)
// 锁定屏幕为竖屏模式,不能设备如何旋转,屏幕都不会切换到横屏模式。
window.screen.lockOrientation([“portrait-primary”,“portrait-secondary”]);
// 锁定屏幕为横屏模式,无能设备如何旋转,屏幕都不会切换到竖屏模式。
window.screen.lockOrientation([“landscape-primary”,“landscape-secondary”]);
// 取消屏幕的锁屏,屏幕回到原始状态,
window.screen.unlockOrientation();

manifest.json里控制:
"orientation": [
"portrait-primary",
"portrait-secondary"
]
该文转载地址:http://ask.dcloud.net.cn/article/522

继续阅读 »

解决横屏问题:
(转载)
真机运行时,manifest并不会实时生效,要打包才生效。
横屏有3个层级:

  1. 手机禁止横屏
  2. 手机允许横屏,但manifest禁止横屏
  3. 手机允许横屏、manifest允许横屏,但页面代码禁止横屏。

js里控制:
//仅支持横屏显示
plus.screen.lockOrientation("landscape-primary");
//仅支持横屏反方向显示
plus.screen.lockOrientation('landscape-secondary');
//仅支持竖屏显示
plus.screen.lockOrientation("portrait-primary");
//仅支持竖屏反方向显示
plus.screen.lockOrientation("portrait-secondary");

或(该方式未经测试)
// 锁定屏幕为竖屏模式,不能设备如何旋转,屏幕都不会切换到横屏模式。
window.screen.lockOrientation([“portrait-primary”,“portrait-secondary”]);
// 锁定屏幕为横屏模式,无能设备如何旋转,屏幕都不会切换到竖屏模式。
window.screen.lockOrientation([“landscape-primary”,“landscape-secondary”]);
// 取消屏幕的锁屏,屏幕回到原始状态,
window.screen.unlockOrientation();

manifest.json里控制:
"orientation": [
"portrait-primary",
"portrait-secondary"
]
该文转载地址:http://ask.dcloud.net.cn/article/522

收起阅读 »

承接MUI App,软件开发,微信开发

mui 招聘 外包 移动APP

腾印网络致力于中小企业电商服务,软件开发,系统开发,提供一站式整体解决方案。
服务范围包括:网站建设,APP开发,微信开发,软件定制,系统定制 等

服务产品
PC端:网站建设,行业网站,电商平台
移动端:手机网站,手机APP
微信开发:微官网,微商城,公众号,小程序
系统开发:直播系统,点播系统,进销存系统 等

成功案例:电商类、视频类、在线教育类、生活服务类、游戏下载类等APP

QQ:1776609688

继续阅读 »

腾印网络致力于中小企业电商服务,软件开发,系统开发,提供一站式整体解决方案。
服务范围包括:网站建设,APP开发,微信开发,软件定制,系统定制 等

服务产品
PC端:网站建设,行业网站,电商平台
移动端:手机网站,手机APP
微信开发:微官网,微商城,公众号,小程序
系统开发:直播系统,点播系统,进销存系统 等

成功案例:电商类、视频类、在线教育类、生活服务类、游戏下载类等APP

QQ:1776609688

收起阅读 »