HBuilderX

HBuilderX

极客开发工具
uni-app

uni-app

开发一次,多端覆盖
uniCloud

uniCloud

云开发平台
HTML5+

HTML5+

增强HTML5的功能体验
MUI

MUI

上万Star的前端框架

新版Hbuilder运行浏览器时自动带上一个GET参数

浏览器运行

软件是新装的,也还原过配置,都没用,新版运行时带了参数,各种浏览器都会带上参数,老版的没有这个问题
图片中带参数的是8.8版本的
不带参数的是7.3版本的
虽然不影响测试,但是我这种强迫症根本受不了呀,希望有人能告诉我怎么去掉那个参数,谢谢

软件是新装的,也还原过配置,都没用,新版运行时带了参数,各种浏览器都会带上参数,老版的没有这个问题
图片中带参数的是8.8版本的
不带参数的是7.3版本的
虽然不影响测试,但是我这种强迫症根本受不了呀,希望有人能告诉我怎么去掉那个参数,谢谢

mui编写的全静态网站源码,完美解决下拉刷新。宣传可赚钱

下拉刷新


这是一套,可以使用了的mui网站源码。调用的API稳定可靠,也是开源的商业公司出品!大家可以上传到自己的服务器上面
修改config.js文件将
var myuid=1;/分红账号ID,唯一要修改的地方,否则无法赚钱/
替换成您的myuid
如果有人在上面消费就可以赚钱10%的佣金。
测试地址:点我查看效果

继续阅读 »


这是一套,可以使用了的mui网站源码。调用的API稳定可靠,也是开源的商业公司出品!大家可以上传到自己的服务器上面
修改config.js文件将
var myuid=1;/分红账号ID,唯一要修改的地方,否则无法赚钱/
替换成您的myuid
如果有人在上面消费就可以赚钱10%的佣金。
测试地址:点我查看效果

收起阅读 »

h5+ 跨平台 app开发学习路线【附教程】

App

《JavaScript 快速提高视频教程》 js基础快速提高课程 【免费】
http://www.hcoder.net/course/info_229.html

《MUI 视频教程》【免费】
http://www.hcoder.net/course/info_211.html

《h.js 视频教程》【免费】
http://www.hcoder.net/tutorials/info_147.html

《HTML 5 + 开发教程》【免费】
http://www.hcoder.net/course/info_212.html

《APP开发实例教程 - 窗口切换 》【免费】
http://www.hcoder.net/course/info_218.html

《HBuilder 8.0.1 APP开发 - 新功能全接触》【免费】
http://www.hcoder.net/course/info_227.html

--------- 实战收费教程 ------------------------

MUI、H5+ APP 实战教程 - 仿《有道词典》
https://ke.qq.com/course/194834

H5+ 跨平台APP开发电商项目实战教程 -《仿京东优选商城》
https://ke.qq.com/course/225830

更多课程中心
http://www.hcoder.net/course

继续阅读 »

《JavaScript 快速提高视频教程》 js基础快速提高课程 【免费】
http://www.hcoder.net/course/info_229.html

《MUI 视频教程》【免费】
http://www.hcoder.net/course/info_211.html

《h.js 视频教程》【免费】
http://www.hcoder.net/tutorials/info_147.html

《HTML 5 + 开发教程》【免费】
http://www.hcoder.net/course/info_212.html

《APP开发实例教程 - 窗口切换 》【免费】
http://www.hcoder.net/course/info_218.html

《HBuilder 8.0.1 APP开发 - 新功能全接触》【免费】
http://www.hcoder.net/course/info_227.html

--------- 实战收费教程 ------------------------

MUI、H5+ APP 实战教程 - 仿《有道词典》
https://ke.qq.com/course/194834

H5+ 跨平台APP开发电商项目实战教程 -《仿京东优选商城》
https://ke.qq.com/course/225830

更多课程中心
http://www.hcoder.net/course

收起阅读 »

强烈建议官方接入极光推送,强烈建议官方接入极光推送,强烈建议官方接入极光推送

个推的传透消息很难用,小米推送的ios很不稳定。
强烈建议官方接入极光推送,强烈建议官方接入极光推送,强烈建议官方接入极光推送强烈建议官方接入极光推送,强烈建议官方接入极光推送,强烈建议官方接入极光推送强烈建议官方接入极光推送,强烈建议官方接入极光推送,强烈建议官方接入极光推送强烈建议官方接入极光推送,强烈建议官方接入极光推送,强烈建议官方接入极光推送强烈建议官方接入极光推送,强烈建议官方接入极光推送,强烈建议官方接入极光推送。

继续阅读 »

个推的传透消息很难用,小米推送的ios很不稳定。
强烈建议官方接入极光推送,强烈建议官方接入极光推送,强烈建议官方接入极光推送强烈建议官方接入极光推送,强烈建议官方接入极光推送,强烈建议官方接入极光推送强烈建议官方接入极光推送,强烈建议官方接入极光推送,强烈建议官方接入极光推送强烈建议官方接入极光推送,强烈建议官方接入极光推送,强烈建议官方接入极光推送强烈建议官方接入极光推送,强烈建议官方接入极光推送,强烈建议官方接入极光推送。

收起阅读 »

阿里云前端工程化工具 Dawn 正式开源啦

Gulp

Banner

Dawn

Dawn 取「黎明、破晓」之意,原为「阿里云·业务运营团队」内部的前端构建和工程化工具,现已完全开源。它通过 pipeline 和 middleware 将开发过程抽象为相对固定的阶段和有限的操作,简化并统一了开发人员的日常构建与开发相关的工作。

npm NPM Version Build Status Coverage Status npm

项目地址:https://github.com/alibaba/dawn (感兴趣请赏个 Star)

特点

  • 采用中间件技术,封装常用功能,易于扩展,方便重用
  • 支持 pipeline 让多个 task 协同完成构建任务
  • 简单、一致的命令行接口,易于开发人员使用
  • 根据模板快速生成项目工程结构
  • 支持基于「中心服务」管理中件间和工程模板
  • 支持搭建私有中心服务,并统一下发构建规则,易于团队统一管理

安装

$ npm install dawn -g

使用

# 1. 创建 & 初始化  
$ dn init -t front  

# 2. 开发 & 实时编译  
$ dn dev  

# 3. 语法检查 & 测试  
$ dn test  

# 4. 构建 & 打包  
$ dn build

示例(.dawn.yml 或 .dawn 目录)

# 启动开发服务  
dev:  
  - name: webpack  
    entry: ./src/*.js  
    template: ./assets/*.html  
    watch: true  
  - name: server  
    port: 8001  

# 直接构建  
buid:  
  - name: webpack  
    entry: ./src/*.js  
    template: ./assets/*.html

文档

-- end --

继续阅读 »

Banner

Dawn

Dawn 取「黎明、破晓」之意,原为「阿里云·业务运营团队」内部的前端构建和工程化工具,现已完全开源。它通过 pipeline 和 middleware 将开发过程抽象为相对固定的阶段和有限的操作,简化并统一了开发人员的日常构建与开发相关的工作。

npm NPM Version Build Status Coverage Status npm

项目地址:https://github.com/alibaba/dawn (感兴趣请赏个 Star)

特点

  • 采用中间件技术,封装常用功能,易于扩展,方便重用
  • 支持 pipeline 让多个 task 协同完成构建任务
  • 简单、一致的命令行接口,易于开发人员使用
  • 根据模板快速生成项目工程结构
  • 支持基于「中心服务」管理中件间和工程模板
  • 支持搭建私有中心服务,并统一下发构建规则,易于团队统一管理

安装

$ npm install dawn -g

使用

# 1. 创建 & 初始化  
$ dn init -t front  

# 2. 开发 & 实时编译  
$ dn dev  

# 3. 语法检查 & 测试  
$ dn test  

# 4. 构建 & 打包  
$ dn build

示例(.dawn.yml 或 .dawn 目录)

# 启动开发服务  
dev:  
  - name: webpack  
    entry: ./src/*.js  
    template: ./assets/*.html  
    watch: true  
  - name: server  
    port: 8001  

# 直接构建  
buid:  
  - name: webpack  
    entry: ./src/*.js  
    template: ./assets/*.html

文档

-- end --

收起阅读 »

做聊天页面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

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

收起阅读 »