Open geqianqian-shihan opened 6 years ago
1、字体行高 小程序在字体小于10px时,即使行高设置为1 ,字体占据位置也是10px;安卓bug 2、华为手机 华为手机删除小程序 不会删除保存的数据 3、cover-view 覆盖在原生组件之上的文本视图,可覆盖的原生组件包括map、video、canvas、camera、live-player、live-pusher,只支持嵌套cover-view、cover-image,可在cover-view中使用button。 4、打开小程序 需要真机测试
5、wx.openSetting 已经废弃
twoList:[{
"id": 1,
"name": "应季鲜果",
"count": "1",
"twodata": [{
"id": 11,
"name": "鸡脆骨"
},{
"id": 12,
"name": "鸡爪"
}]
}]
<view class="pad10" wx:for="{{twoList}}" wx:key="id">
<view>
{{index+1}}、{{item.name}}
</view>
<view wx:for="{{item.twodata}}" wx:for-item="twodata" wx:key="id">
----{{twodata.name}}---{{item.name}}
</view>
</view>
我们在wxml代码里,很明显的看到有两个wx:for的控制属性,在二层循环的JSON代码里,我们看每个单数组里还有一级数据twodata,这里是需要再循环渲染到页面上的,在第一层数据里,直接再循环item.twodata即可,请记得一定要带上花括号。 在第二层的循环里,建议把当前项的变量名改为其他,即在wxml代码里看到的wx:for-item="twodata",因为默认的当前项的变量名为item,如果不改换其他的话,你是拿不到第一层循环的数据的,因为被第二层的变量名覆盖了。 所以我们在wxml代码里,在第二层循环时,可以看到还可以循环第一层的值,即----{{twodata.name}}---{{item.name}}。
为什么会有wx:key的出现呢,官方给的解释是,如果列表中项目的位置会动态改变或者有新的项目添加到列表中,并且希望列表中的项目保持自己的特征和状态(如 input 中的输入内容,switch 的选中状态),需要使用 wx:key 来指定列表中项目的唯一的标识符。 当数据改变触发渲染层重新渲染的时候,会校正带有 key 的组件,框架会确保他们被重新排序,而不是重新创建,以确保使组件保持自身的状态,并且提高列表渲染时的效率。 wx:key 的值以两种形式提供
1、wx:key="property" 其中property是代表在 for 循环的 array 中 item 的某个 property,该 property 的值需要是列表中唯一的字符串或数字,且不能动态改变。类似于字典的key值
2、wx:key="this", 保留关键字 this 代表在 for 循环中的 item 本身,这种表示需要 item 本身是一个唯一的字符串或者数字,如:当数据改变触发渲染层重新渲染的时候,会校正带有 key 的组件,框架会确保他们被重新排序,而不是重新创建,以确保使组件保持自身的状态,并且提高列表渲染时的效率。
e.target是返回触发事件的对象 e.currentTarget返回的是绑定事件的对象。 认真看这段话: 通常情况下target和currentTarget是一致的,我们只要使用target即可,但有一种情况必须区分这两者的关系,那就是在父子嵌套的关系中,父元素绑定了事件,单击了子元素(根据事件流,在不阻止事件流的前提下他会传递至父元素,导致父元素的事件处理函数执行),这时候currentTarget指向的是父元素,因为他是绑定事件的对象,而target指向了子元素,因为他是触发事件的那个具体对象 。 在你的第一种情况中,可能触发这个事件的并非是这个子元素。所以也就没有返回你想要的那个target。 而在你的第二种情况中,可能恰巧触发这个事件的就是含有data-name的这个text标签,所以也就返回了你想看到的那个target
event.currentTarget.dataset.postid event.target.dataset.postid
.btn{
boder: none;
border-radius: none;
}
.btn::after{
boder: none;
border-radius: none;
}
const canvasImg = (cObj, callback) => {
wx.showLoading({
title: '加载图片中...',
})
// canvasId, imgurl, signIn
let picPath = cObj.imgurl;
let user = cObj.usrInfo;
wx.getImageInfo({
src: cObj.imgurl,
success: function (res) {
console.log(res);
// 使用 wx.createContext 获取绘图上下文 context
var context = wx.createCanvasContext(cObj.canvasId);
var cxtW = 310;
var cxtH = 248;
var imgW = res.width;
var imgH = res.height;
//圆角半径
var r = 10;
context.save();
//安卓系统有问题
// var pattern = context.createPattern(picPath, "no-repeat");
//context.beginPath();
//context.moveTo( r, 0);
// context.arcTo(cxtW, 0, cxtW, cxtH, r);
//context.arcTo(cxtW, cxtH, 0, cxtH, r);
//context.arcTo(0, cxtH, 0, 0, r);
//context.arcTo(0, 0, cxtW, 0, r);
// context.closePath();
//context.clip();
//改用
//roundRect(r, w, h, x, y, context);
//context.clip();
//小程序目前只支持一次裁切 改用展示图片 对图片做圆角
context.drawImage(picPath, 0, 0, imgW, imgH, 0, 0, cxtW, cxtH);
context.restore();
var rpxToPxRatio = app.globalData.rpxToPxRatio;
// 是否添加签名
if (cObj.signIn) {
var avatarWidth = 68 * rpxToPxRatio; // 头像宽度
var avatarHeight = 68 * rpxToPxRatio; // 头像高度
var avatarMarginLeft = 20 * rpxToPxRatio; // 头像左间距
var avatarMarginBottom = 20 * rpxToPxRatio; // 头像底间距
var nicknameFontSize = 26 * rpxToPxRatio; // 昵称字号
var phoneNumberFontSize = 26 * rpxToPxRatio; // 号码字号
var middleFontMargin = 15 * rpxToPxRatio; // 文字中间间距
// 线性阴影
var grd = context.createLinearGradient(0, cxtH, 0, cxtH - avatarMarginBottom * 2 - avatarHeight);
grd.addColorStop(0, 'rgba(0, 0, 0, 0.8)');
grd.addColorStop(0.63, 'rgba(0, 0, 0, 0.4)');
grd.addColorStop(1, 'rgba(0, 0, 0, 0)');
context.setFillStyle(grd);
context.fillRect(0, cxtH - avatarMarginBottom * 2 - avatarHeight, cxtW, cxtH);
// 用户头像绘制
context.arc(avatarMarginLeft + avatarWidth / 2, cxtH - avatarMarginBottom - avatarHeight / 2, avatarWidth / 2, 0, Math.PI * 2, false);
context.clip();
context.drawImage(user.avatarUrl, avatarMarginLeft, cxtH - avatarMarginBottom - avatarHeight, avatarWidth, avatarHeight);
// 设置姓名样式
context.restore();
context.setFillStyle('#fefefe');
context.setFontSize(nicknameFontSize);
// 用户姓名绘制
context.fillText(user.nickName, 2 * avatarMarginLeft + avatarWidth, cxtH - avatarMarginBottom - avatarHeight + nicknameFontSize);
// 设置手机号样式
context.setFontSize(phoneNumberFontSize);
// 绘制手机号
// user.phone_number
context.fillText('1234561231', 2 * avatarMarginLeft + avatarWidth, cxtH - avatarMarginBottom - avatarHeight + nicknameFontSize + phoneNumberFontSize + middleFontMargin);
// 是否是打卡
if (cObj.clockIn) {
// 设置打卡样式
let msg = cObj.clockInMsg;
context.restore();
context.setFillStyle('#fafafa');
context.setFontSize(nicknameFontSize);
context.fillText(msg.time, 250, 210);
context.fillText("坚持打卡第" + msg.times + "天", 210, 230);
// 用户打卡信息绘制
context.setFillStyle('#aaa');
context.fillText("早起打卡 " + msg.data, 180, 20);
}
}
console.log("success");
context.draw(false, function (res) {
console.log("success");
wx.canvasToTempFilePath({
x: 0,
y: 0,
width: cxtW,
height: cxtH,
quality: 1.0,
canvasId: cObj.canvasId,
success: function (res) {
wx.hideLoading()
var tempFilePath = res.tempFilePath
// 签名图片显示
callback(tempFilePath);
},
fail: function (res) {
console.log('canvasSigningIndex fail:', res)
},
complete: function (res) {
console.log('canvasSigningIndex complete:', res)
}
})
})
},
fail: function (res) {
console.log(res);
}
});
}
//保存canvas图片
const saveImage = (imgUrl) => {
wx.saveImageToPhotosAlbum({
filePath: imgUrl,
success(res) {
wx.showToast({
title: '图片已保存到相册',
icon: 'success',
duration: 2000
})
},
fail(res) {
wx.showToast({
title: '图片保存失败',
icon: 'none',
duration: 2000
})
}
})
}
// 绘制圆角图形
const roundRect = (r, w, h, x, y, context,cObj) =>{
// 开始绘制
// context.save();
context.beginPath();
context.setFillStyle('#ffffff');
context.arc(x + r, y + r, r, Math.PI, Math.PI * 1.5);
context.moveTo(x + r, y);
context.lineTo(x + w - r, y);
context.lineTo(x + w, y + r);
context.arc(x + w - r, y + r, r, Math.PI * 1.5, Math.PI * 2);
context.lineTo(x + w, y + h - r);
context.lineTo(x + w - r, y + h);
context.arc(x + w - r, y + h - r, r, 0, Math.PI * 0.5);
context.lineTo(x + r, y + h);
context.lineTo(x, y + h - r);
context.arc(x + r, y + h - r, r, Math.PI * 0.5, Math.PI);
context.lineTo(x, y + r);
context.lineTo(x + r, y);
context.closePath();
return this;
}
文字折行,打印机输出文字 参考文章:张鑫旭博客
const setQcode = (cObj, callback) => {
wx.getImageInfo({
src: cObj.extra.channelQrcodeUrl,
success: function (res) {
var rpxToPxRatio = app.globalData.rpxToPxRatio;
// 使用 wx.createContext 获取绘图上下文 context
var context = wx.createCanvasContext(cObj.canvasId);
var cxtW = 680 * rpxToPxRatio;
var cxtH = (544 + 169) * rpxToPxRatio;
context.width = cxtW;
context.height = cxtH + 500 * rpxToPxRatio;
context.save();
context.setFillStyle('#fff');
context.fillRect(0, 544 * rpxToPxRatio, 680 * rpxToPxRatio, 169 * rpxToPxRatio);
context.restore();
// 文字
context.save();
var nicknameFontSize = 26 * rpxToPxRatio; // 昵称字号
context.setFillStyle('#333');
context.setFontSize(nicknameFontSize);
// 文字折行
// cObj.imgTit
let titArr = escape2Html(cObj.imgTit).split('');
let text = ''; //循环文案
let lineHeight = 40*rpxToPxRatio;
let maxWidth = 400 * rpxToPxRatio;
let y = (544 + 70) * rpxToPxRatio;
let lineTimes = 0;
for(let n = 0;n < titArr.length;n++){
text = text + titArr[n];
let titWidth = context.measureText(text).width;
if (titWidth > maxWidth && n > 0){
context.fillText(text, 40 * rpxToPxRatio, y);
text = titArr[n];
y += lineHeight;
++lineTimes;
}else{
text;
}
if (lineTimes > 1) {
break;
}
}
if(lineTimes<2){
context.fillText(text, 40 * rpxToPxRatio, y);
}
context.restore();
// 图片title
// context.fillText(cObj.imgTit, 40 * rpxToPxRatio, (544 + 60) * rpxToPxRatio,400*rpxToPxRatio);
// 二维码
context.save();
context.beginPath();
context.drawImage(res.path, (680 - 129 - 20) * rpxToPxRatio, (544 +169 - 20 - 129) * rpxToPxRatio, 129 * rpxToPxRatio, 129 * rpxToPxRatio);
context.restore();
context.draw(true, function (res) {
wx.canvasToTempFilePath({
x: 0,
y: 0,
width: cxtW,
height: cxtH,
quality: 1.0,
canvasId: cObj.canvasId,
success: function (temp) {
wx.hideLoading();
var tempFilePath = temp.tempFilePath;
// 签名图片显示
callback(tempFilePath);
// console.log('canvasSigningIndex success:', tempFilePath)
},
fail: function (tempFile) {
wx.showToast({
title: '获取二维码失败,请稍后重试',
icon: 'none'
})
},
complete: function (tempCom) {
// console.log('canvasSigningIndex complete:', tempCom)
}
})
})
}
});
}
context.save();
context.beginPath();
context.setFontSize(phoneNumberFontSize);//字体大小
context.setLineWidth(4);//线条宽度
context.setShadow(2,2,2,'#000');//设置阴影
context.setStrokeStyle('#33ff66');//线条样式
context.setFillStyle('#fff');
context.fillText("早起打卡 " + msg.date, 400 * rpxToPxRatio, 48 * rpxToPxRatio);
context.closePath();
context.stroke();
canvas.save();和canvas.restore();是两个相互匹配出现的,作用是用来保存画布的状态和取出保存的状态的。保存绘制状态和防止污染状态栈。 save()保存的只是CanvasRenderingContext2D对象的状态以及对象的所有属性,并不包括这个对象上绘制的图形。 当我们对画布进行旋转,缩放,平移等操作的时候其实我们是想对特定的元素进行操作,比如图片,一个矩形等,但是当你用canvas的方法来进行这些操作的时候,其实是对整个画布进行了操作,那么之后在画布上的元素都会受到影响,所以我们在操作之前调用canvas.save()来保存画布当前的状态,当操作之后取出之前保存过的状态,这样就不会对其他的元素进行影响
在Canvas环境中绘图时,可以利用所谓的绘图堆栈状态。每个状态随时存储Canvas上下文数据。下面是存储在状态堆栈的数据列表。
CanvasRenderingContext2D的当前属性值主要包括:
著作权归作者所有。 商业转载请联系作者获得授权,非商业转载请注明出处。 原文: https://www.w3cplus.com/canvas/canvas-states.html © w3cplus.com
if (wx.getUpdateManager){
const updateManager = wx.getUpdateManager(); //管理小程序更新
updateManager.onCheckForUpdate(function (res) {
// 请求完新版本信息的回调
// console.log(res.hasUpdate)
})
updateManager.onUpdateReady(function () {
wx.showModal({
title: '更新提示',
content: '新版本已经准备好,是否重启应用?',
success: function (res) {
if (res.confirm) {
// 新的版本已经下载好,调用 applyUpdate 应用新版本并重启
updateManager.applyUpdate()
}
}
})
})
updateManager.onUpdateFailed(function () {
// 新的版本下载失败
})
}
1、封装wx.request来判断是否断网,及断网后记录本次请求; 2、当网络断开时,你可以给用户一个提示,告诉用户网络连接有问题,重新连接之后,让用户自己去点击加载的按钮,也就相当于重新加载了
function wxRequest (obj, cb, page, type) {
var isOne = true
var cachFn = function () {
wx.request({
url: obj.url,
data: obj.data || {},
method: obj.method || 'GET',
success: function (res) {
cb.call(page, res)
if (!page.data.isNet) {
page.setData({
isNet: true
})
}
},
// fail执行时当断网处理
fail: function () {
// 防止fail 有时会执行两次,影响渲染
if (!isOne) {
return
}
page.setData({
isNet: false,
isRequested: false
})
// 记录本次请求,加载时,执行page实例的reloadFn即可
page.reloadFn = wxRequest(obj, cb, page, 1)
isOne = false
}
})
}
if (type) {
page.isRequested = true
}
return type ? cachFn : cachFn()
}
let data = {
url: '',
data: {},
method: ''
}
wxTools.wxRequest(data, (res) => {
// 数据渲染
this.setData({})
}, this)
1、首屏加载时间 2s 下载小程序代码包 --> 打开应用 1M 以内 平均小于1.5s
2、渲染 渲染单页时间不超过值 IOS -- 200ms Android -- 500ms
3、交互
扫一扫 | 蓝牙 | 收货地址 |
---|---|---|
到店订餐 | 会议签到 | 电商购物 |
扫描商品 | 到店领取优惠 | 寄送快递 |
开启共享单车 | 手机开门 | 寄送回执 |
公司打卡 |
启动快、感受快、操作快
1、需先调用login接口 2、需要用户主动触发才能发起获取手机号接口,需用
// 声明函数 common.js
const countDown = (that) =>{
let countDownNum =that.data.countDown
console.log(countDownNum)
if (countDownNum == 0) {
that.setData({
requestCode: false
});
return;
}
let timer = setTimeout(function(){
--countDownNum;
that.setData({
countDown: countDownNum
});
countDown(that);
}, 100);
}
const clearTimeout = (timer) => {
clearTimeout(timer);
}
//引用函数页
changeTime: function(){
let that = this;
clearTimeout();
util.countDown(that);
}
//子组件
<template></template>
export default {
props:{},
data():{},
methods:{
getVerifyCode(){
thiz.$parent.getVerifyCode(phoneNumber,interval);
}
}
}
<verify :showPop="isShow" :inputVal="phoneNumber" :parent-cancel="cancel" @parent-binding="binding(msg)"
@parent-msg="showToast(msg)"></verify>
//父组件
import Verify from "@/components/verify-phone";
getVerifyCode(phoneNumber,interval){
clearInterval(interval)
}
要上述问题,就需要一个能支持多个平台网络库,用户层提供统一的API,将平台差异在底层屏蔽。而 Fly.js就是这酱紫的一个网络库,为了方便axios使用者迁移,fly.js API设计风格和axios相似(但不完全相同)!
Fly.js 通过在不同 JavaScript 运行时通过在底层切换不同的 Http Engine来实现多环境支持,但同时对用户层提供统一、标准的Promise [API。不仅如此,Fly.js还支持请求/响应拦截器、自动转化JSON、请求转发等功能,详情请参考:https://github.com/wendux/fly 。
var Fly=require("flyio/dist/npm/wx")
var fly=new Fly
... //添加全局配置、拦截器等
Vue.prototype.$http=fly //将fly实例挂在vue原型上
//组件中使用
this.$http.get("/test",{xx:6}).then((d)=>{
//输出请求数据
console.log(d.data)
//输出响应头
console.log(d.header)
}).catch(err=>{
console.log(err.status,err.message)
})
Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。
new Vue({
// state
data () {
return {
count: 0
}
},
// view
template: `
<div>{{ count }}</div>
`,
// actions
methods: {
increment () {
this.count++
}
}
})
这个状态自管理应用包含以下几个部分:
state,驱动应用的数据源;
view,以声明方式将 state 映射到视图;
actions,响应在 view 上的用户输入导致的状态变化。 但是,当我们的应用遇到多个组件共享状态时,单向数据流的简洁性很容易被破坏:
多个视图依赖于同一状态。
来自不同视图的行为需要变更同一状态。 对于问题一,传参的方法对于多层嵌套的组件将会非常繁琐,并且对于兄弟组件间的状态传递无能为力。对于问题二,我们经常会采用父子组件直接引用或者通过事件来变更和同步状态的多份拷贝。以上的这些模式非常脆弱,通常会导致无法维护的代码。
因此,我们为什么不把组件的共享状态抽取出来,以一个全局单例模式管理呢?在这种模式下,我们的组件树构成了一个巨大的“视图”,不管在树的哪个位置,任何组件都能获取状态或者触发行为!
另外,通过定义和隔离状态管理中的各种概念并强制遵守一定的规则,我们的代码将会变得更结构化且易维护。
这就是 Vuex 背后的基本思想,借鉴了 Flux、Redux、和 The Elm Architecture。与其他模式不同的是,Vuex 是专门为 Vue.js 设计的状态管理库,以利用 Vue.js 的细粒度数据响应机制来进行高效的状态更新。
虽然 Vuex 可以帮助我们管理共享状态,但也附带了更多的概念和框架。
更新用户昵称及头像,大概1~2 个小时 open-type="getUserInfo" 可以拿到最新数据
从 wx.getUserInfo 拿到的 nickName 里的 emoji 采用的是 Softbank 编码,而浏览器和小程序支持的是 Unified,我的小程序是自己做了编码转换。
微信小程序中生成二维码工具:weapp.qrcode.js 通过js生成了二维码,那就是到 =>转成图片生成临时文件路径=>previewImage预览图片=>长按转发, 这个是不是相当于只转发了一张图片
原作者地址:微信小程序-template模板使用
模板
一、定义模板 1、新建一个template文件夹用来管理项目中所有的模板; 2、新建一个courseList.wxml文件来定义模板; 3、使用name属性,作为模板的名字。然后在内定义代码片段。
注意: a.可以看到一个.wxml文件中可以定义多个模板,只需要通过name来区分; b.模板中的数据都是展开之后的属性。
二、使用模板 1、使用 is 属性,声明需要的使用的模板。
2、将模板所需要的 data 传入,一般我们都会使用列表渲染。
注意: a.可以通过表达式来确定使用哪个模板is="{{index%2 === 0 ? 'courseLeft' : 'courseRight'}}" 或者通过wx:if来确定。index是数组当前项的下标。
b. data 是要模板渲染的数据,data="{{...item}}" 写法是ES6的写法,item是wx:for当前项,... 是展开运算符,在模板中不需要再{{item.courseName}} 而是直接{{courseName}} 。
三、模板样式 1、在新建模板的时候同时新建一个courseList.wxss 的文件,与CSS同样的写法控制样式。 2、在需要使用模板的页面 .wxss文件中import进来;或者直接在app.wxss中引入,这样只需要一次引入,其他文件就不用引入了。
模板添加事件 temp.js
使用模板 index.js