添加二维码和颜色选择器两个组件
This commit is contained in:
parent
74f1ef7e6d
commit
8a64af39bb
@ -0,0 +1,733 @@
|
||||
<template>
|
||||
<view v-show="show" class="t-wrapper" @touchmove.stop.prevent="moveHandle">
|
||||
<view class="t-mask" :class="{ active: active }" @click.stop="close"></view>
|
||||
<view class="t-box" :class="{ active: active }">
|
||||
<view class="t-header">
|
||||
<view class="t-header-button" @click="close">取消</view>
|
||||
<view class="t-header-button confrim" @click="confirm">确认</view>
|
||||
</view>
|
||||
<view class="t-color__box"
|
||||
:style="{ background: 'rgb(' + bgcolor.r + ',' + bgcolor.g + ',' + bgcolor.b + ')' }">
|
||||
<view class="t-background boxs" @touchstart="touchstart($event, 0)" @touchmove="touchmove($event, 0)"
|
||||
@touchend="touchend($event, 0)">
|
||||
<view class="t-color-mask"></view>
|
||||
<view class="t-pointer" :style="{ top: site[0].top - 8 + 'px', left: site[0].left - 8 + 'px' }"></view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="t-control__box">
|
||||
<view class="t-control__color">
|
||||
<view class="t-control__color-content"
|
||||
:style="{ background: 'rgba(' + rgba.r + ',' + rgba.g + ',' + rgba.b + ',' + rgba.a + ')' }"></view>
|
||||
</view>
|
||||
<view class="t-control-box__item">
|
||||
<view class="t-controller boxs" @touchstart="touchstart($event, 1)" @touchmove="touchmove($event, 1)"
|
||||
@touchend="touchend($event, 1)">
|
||||
<view class="t-hue">
|
||||
<view class="t-circle" :style="{ left: site[1].left - 12 + 'px' }"></view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="t-controller boxs" @touchstart="touchstart($event, 2)" @touchmove="touchmove($event, 2)"
|
||||
@touchend="touchend($event, 2)">
|
||||
<view class="t-transparency">
|
||||
<view class="t-circle" :style="{ left: site[2].left - 12 + 'px' }"></view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="t-result__box">
|
||||
<view v-if="mode" class="t-result__item">
|
||||
<view class="t-result__box-input">{{ hex }}</view>
|
||||
<view class="t-result__box-text">HEX</view>
|
||||
</view>
|
||||
<template v-else>
|
||||
<view class="t-result__item">
|
||||
<view class="t-result__box-input">{{ rgba.r }}</view>
|
||||
<view class="t-result__box-text">R</view>
|
||||
</view>
|
||||
<view class="t-result__item">
|
||||
<view class="t-result__box-input">{{ rgba.g }}</view>
|
||||
<view class="t-result__box-text">G</view>
|
||||
</view>
|
||||
<view class="t-result__item">
|
||||
<view class="t-result__box-input">{{ rgba.b }}</view>
|
||||
<view class="t-result__box-text">B</view>
|
||||
</view>
|
||||
<view class="t-result__item">
|
||||
<view class="t-result__box-input">{{ rgba.a }}</view>
|
||||
<view class="t-result__box-text">A</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<view class="t-result__item t-select" @click="select">
|
||||
<view class="t-result__box-input">
|
||||
<view>切换</view>
|
||||
<view>模式</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="t-alternative">
|
||||
<view class="t-alternative__item" v-for="(item, index) in colorList" :key="index">
|
||||
<view class="t-alternative__item-content"
|
||||
:style="{ background: 'rgba(' + item.r + ',' + item.g + ',' + item.b + ',' + item.a + ')' }"
|
||||
@click="selectColor(item)">
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
color: {
|
||||
type: Object,
|
||||
default() {
|
||||
return { r: 0, g: 0, b: 0, a: 0 }
|
||||
}
|
||||
},
|
||||
spareColor: {
|
||||
type: Array,
|
||||
default() {
|
||||
return []
|
||||
}
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
show: false,
|
||||
active: false,
|
||||
// rgba 颜色
|
||||
rgba: { r: 0, g: 0, b: 0, a: 1 },
|
||||
// hsb 颜色
|
||||
hsb: { h: 0, s: 0, b: 0 },
|
||||
site: [{ top: 0, left: 0 }, { left: 0 }, { left: 0 }],
|
||||
index: 0,
|
||||
bgcolor: {
|
||||
r: 255,
|
||||
g: 0,
|
||||
b: 0,
|
||||
a: 1
|
||||
},
|
||||
hex: '#000000',
|
||||
mode: true,
|
||||
colorList: [
|
||||
{ r: 244, g: 67, b: 54, a: 1 },
|
||||
{ r: 233, g: 30, b: 99, a: 1 },
|
||||
{ r: 156, g: 39, b: 176, a: 1 },
|
||||
{ r: 103, g: 58, b: 183, a: 1 },
|
||||
{ r: 63, g: 81, b: 181, a: 1 },
|
||||
{ r: 33, g: 150, b: 243, a: 1 },
|
||||
{ r: 3, g: 169, b: 244, a: 1 },
|
||||
{ r: 0, g: 188, b: 212, a: 1 }, {
|
||||
r: 0,
|
||||
g: 150,
|
||||
b: 136,
|
||||
a: 1
|
||||
}, {
|
||||
r: 76,
|
||||
g: 175,
|
||||
b: 80,
|
||||
a: 1
|
||||
}, {
|
||||
r: 139,
|
||||
g: 195,
|
||||
b: 74,
|
||||
a: 1
|
||||
}, {
|
||||
r: 205,
|
||||
g: 220,
|
||||
b: 57,
|
||||
a: 1
|
||||
}, {
|
||||
r: 255,
|
||||
g: 235,
|
||||
b: 59,
|
||||
a: 1
|
||||
}, {
|
||||
r: 255,
|
||||
g: 193,
|
||||
b: 7,
|
||||
a: 1
|
||||
}, {
|
||||
r: 255,
|
||||
g: 152,
|
||||
b: 0,
|
||||
a: 1
|
||||
}, {
|
||||
r: 255,
|
||||
g: 87,
|
||||
b: 34,
|
||||
a: 1
|
||||
}, {
|
||||
r: 121,
|
||||
g: 85,
|
||||
b: 72,
|
||||
a: 1
|
||||
}, {
|
||||
r: 158,
|
||||
g: 158,
|
||||
b: 158,
|
||||
a: 1
|
||||
}, {
|
||||
r: 0,
|
||||
g: 0,
|
||||
b: 0,
|
||||
a: 0.5
|
||||
}, {
|
||||
r: 0,
|
||||
g: 0,
|
||||
b: 0,
|
||||
a: 0
|
||||
},]
|
||||
};
|
||||
},
|
||||
created() {
|
||||
this.rgba = this.color;
|
||||
if (this.spareColor.length !== 0) {
|
||||
this.colorList = this.spareColor;
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
/**
|
||||
* 初始化
|
||||
*/
|
||||
init() {
|
||||
// hsb 颜色
|
||||
this.hsb = this.rgbToHex(this.rgba);
|
||||
this.setValue(this.rgba);
|
||||
},
|
||||
moveHandle() { },
|
||||
open() {
|
||||
this.show = true;
|
||||
this.$nextTick(() => {
|
||||
this.init();
|
||||
setTimeout(() => {
|
||||
this.active = true;
|
||||
setTimeout(() => {
|
||||
this.getSelectorQuery();
|
||||
}, 350)
|
||||
}, 50)
|
||||
})
|
||||
|
||||
},
|
||||
close() {
|
||||
this.active = false;
|
||||
this.$nextTick(() => {
|
||||
setTimeout(() => {
|
||||
this.show = false;
|
||||
}, 500)
|
||||
})
|
||||
},
|
||||
confirm() {
|
||||
this.close();
|
||||
this.$emit('confirm', {
|
||||
rgba: this.rgba,
|
||||
hex: this.hex
|
||||
})
|
||||
},
|
||||
// 选择模式
|
||||
select() {
|
||||
this.mode = !this.mode
|
||||
},
|
||||
// 常用颜色选择
|
||||
selectColor(item) {
|
||||
this.setColorBySelect(item)
|
||||
},
|
||||
touchstart(e, index) {
|
||||
const { pageX, pageY, clientX, clientY } = e.touches[0];
|
||||
this.pageX = clientX;
|
||||
this.pageY = clientY;
|
||||
this.setPosition(this.pageX, this.pageY, index);
|
||||
},
|
||||
touchmove(e, index) {
|
||||
const { pageX, pageY, clientX, clientY } = e.touches[0];
|
||||
this.moveX = clientX;
|
||||
this.moveY = clientY;
|
||||
this.setPosition(this.moveX, this.moveY, index);
|
||||
},
|
||||
touchend(e, index) {
|
||||
},
|
||||
/**
|
||||
* 设置位置
|
||||
*/
|
||||
setPosition(x, y, index) {
|
||||
this.index = index;
|
||||
const {
|
||||
top,
|
||||
left,
|
||||
width,
|
||||
height
|
||||
} = this.position[index];
|
||||
// 设置最大最小值
|
||||
|
||||
this.site[index].left = Math.max(0, Math.min(parseInt(x - left), width));
|
||||
if (index === 0) {
|
||||
this.site[index].top = Math.max(0, Math.min(parseInt(y - top), height));
|
||||
// 设置颜色
|
||||
this.hsb.s = parseInt((100 * this.site[index].left) / width);
|
||||
this.hsb.b = parseInt(100 - (100 * this.site[index].top) / height);
|
||||
this.setColor();
|
||||
this.setValue(this.rgba);
|
||||
} else {
|
||||
this.setControl(index, this.site[index].left);
|
||||
}
|
||||
},
|
||||
/**
|
||||
* 设置 rgb 颜色
|
||||
*/
|
||||
setColor() {
|
||||
const rgb = this.HSBToRGB(this.hsb);
|
||||
this.rgba.r = rgb.r;
|
||||
this.rgba.g = rgb.g;
|
||||
this.rgba.b = rgb.b;
|
||||
},
|
||||
/**
|
||||
* 设置二进制颜色
|
||||
* @param {Object} rgb
|
||||
*/
|
||||
setValue(rgb) {
|
||||
this.hex = '#' + this.rgbToHex(rgb);
|
||||
},
|
||||
setControl(index, x) {
|
||||
const {
|
||||
top,
|
||||
left,
|
||||
width,
|
||||
height
|
||||
} = this.position[index];
|
||||
|
||||
if (index === 1) {
|
||||
this.hsb.h = parseInt((360 * x) / width);
|
||||
this.bgcolor = this.HSBToRGB({
|
||||
h: this.hsb.h,
|
||||
s: 100,
|
||||
b: 100
|
||||
});
|
||||
this.setColor()
|
||||
} else {
|
||||
this.rgba.a = (x / width).toFixed(1);
|
||||
}
|
||||
this.setValue(this.rgba);
|
||||
},
|
||||
/**
|
||||
* rgb 转 二进制 hex
|
||||
* @param {Object} rgb
|
||||
*/
|
||||
rgbToHex(rgb) {
|
||||
let hex = [rgb.r.toString(16), rgb.g.toString(16), rgb.b.toString(16)];
|
||||
hex.map(function (str, i) {
|
||||
if (str.length == 1) {
|
||||
hex[i] = '0' + str;
|
||||
}
|
||||
});
|
||||
return hex.join('');
|
||||
},
|
||||
setColorBySelect(getrgb) {
|
||||
const {
|
||||
r,
|
||||
g,
|
||||
b,
|
||||
a
|
||||
} = getrgb;
|
||||
let rgb = {}
|
||||
rgb = {
|
||||
r: r ? parseInt(r) : 0,
|
||||
g: g ? parseInt(g) : 0,
|
||||
b: b ? parseInt(b) : 0,
|
||||
a: a ? a : 0,
|
||||
};
|
||||
this.rgba = rgb;
|
||||
this.hsb = this.rgbToHsb(rgb);
|
||||
this.changeViewByHsb();
|
||||
},
|
||||
changeViewByHsb() {
|
||||
const [a, b, c] = this.position;
|
||||
this.site[0].left = parseInt(this.hsb.s * a.width / 100);
|
||||
this.site[0].top = parseInt((100 - this.hsb.b) * a.height / 100);
|
||||
this.setColor(this.hsb.h);
|
||||
this.setValue(this.rgba);
|
||||
this.bgcolor = this.HSBToRGB({
|
||||
h: this.hsb.h,
|
||||
s: 100,
|
||||
b: 100
|
||||
});
|
||||
|
||||
this.site[1].left = this.hsb.h / 360 * b.width;
|
||||
this.site[2].left = this.rgba.a * c.width;
|
||||
|
||||
},
|
||||
/**
|
||||
* hsb 转 rgb
|
||||
* @param {Object} 颜色模式 H(hues)表示色相,S(saturation)表示饱和度,B(brightness)表示亮度
|
||||
*/
|
||||
HSBToRGB(hsb) {
|
||||
let rgb = {};
|
||||
let h = Math.round(hsb.h);
|
||||
let s = Math.round((hsb.s * 255) / 100);
|
||||
let v = Math.round((hsb.b * 255) / 100);
|
||||
if (s == 0) {
|
||||
rgb.r = rgb.g = rgb.b = v;
|
||||
} else {
|
||||
let t1 = v;
|
||||
let t2 = ((255 - s) * v) / 255;
|
||||
let t3 = ((t1 - t2) * (h % 60)) / 60;
|
||||
if (h == 360) h = 0;
|
||||
if (h < 60) {
|
||||
rgb.r = t1;
|
||||
rgb.b = t2;
|
||||
rgb.g = t2 + t3;
|
||||
} else if (h < 120) {
|
||||
rgb.g = t1;
|
||||
rgb.b = t2;
|
||||
rgb.r = t1 - t3;
|
||||
} else if (h < 180) {
|
||||
rgb.g = t1;
|
||||
rgb.r = t2;
|
||||
rgb.b = t2 + t3;
|
||||
} else if (h < 240) {
|
||||
rgb.b = t1;
|
||||
rgb.r = t2;
|
||||
rgb.g = t1 - t3;
|
||||
} else if (h < 300) {
|
||||
rgb.b = t1;
|
||||
rgb.g = t2;
|
||||
rgb.r = t2 + t3;
|
||||
} else if (h < 360) {
|
||||
rgb.r = t1;
|
||||
rgb.g = t2;
|
||||
rgb.b = t1 - t3;
|
||||
} else {
|
||||
rgb.r = 0;
|
||||
rgb.g = 0;
|
||||
rgb.b = 0;
|
||||
}
|
||||
}
|
||||
return {
|
||||
r: Math.round(rgb.r),
|
||||
g: Math.round(rgb.g),
|
||||
b: Math.round(rgb.b)
|
||||
};
|
||||
},
|
||||
rgbToHsb(rgb) {
|
||||
let hsb = {
|
||||
h: 0,
|
||||
s: 0,
|
||||
b: 0
|
||||
};
|
||||
let min = Math.min(rgb.r, rgb.g, rgb.b);
|
||||
let max = Math.max(rgb.r, rgb.g, rgb.b);
|
||||
let delta = max - min;
|
||||
hsb.b = max;
|
||||
hsb.s = max != 0 ? 255 * delta / max : 0;
|
||||
if (hsb.s != 0) {
|
||||
if (rgb.r == max) hsb.h = (rgb.g - rgb.b) / delta;
|
||||
else if (rgb.g == max) hsb.h = 2 + (rgb.b - rgb.r) / delta;
|
||||
else hsb.h = 4 + (rgb.r - rgb.g) / delta;
|
||||
} else hsb.h = -1;
|
||||
hsb.h *= 60;
|
||||
if (hsb.h < 0) hsb.h = 0;
|
||||
hsb.s *= 100 / 255;
|
||||
hsb.b *= 100 / 255;
|
||||
return hsb;
|
||||
},
|
||||
getSelectorQuery() {
|
||||
const views = uni.createSelectorQuery().in(this);
|
||||
views
|
||||
.selectAll('.boxs')
|
||||
.boundingClientRect(data => {
|
||||
if (!data || data.length === 0) {
|
||||
setTimeout(() => this.getSelectorQuery(), 20)
|
||||
return
|
||||
}
|
||||
this.position = data;
|
||||
// this.site[0].top = data[0].height;
|
||||
// this.site[0].left = 0;
|
||||
// this.site[1].left = data[1].width;
|
||||
// this.site[2].left = data[2].width;
|
||||
this.setColorBySelect(this.rgba);
|
||||
})
|
||||
.exec();
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
spareColor(newVal) {
|
||||
this.colorList = newVal;
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.t-wrapper {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
z-index: 9999;
|
||||
}
|
||||
|
||||
.t-box {
|
||||
width: 100%;
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
padding: 30upx 0;
|
||||
padding-top: 0;
|
||||
background: #fff;
|
||||
transition: all 0.3s;
|
||||
transform: translateY(100%);
|
||||
|
||||
&.active {
|
||||
transform: translateY(0%);
|
||||
}
|
||||
}
|
||||
|
||||
.t-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
width: 100%;
|
||||
height: 100upx;
|
||||
border-bottom: 1px #eee solid;
|
||||
box-shadow: 1px 0 2px rgba(0, 0, 0, 0.1);
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
.t-header-button {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
width: 150upx;
|
||||
height: 100upx;
|
||||
font-size: 30upx;
|
||||
color: #666;
|
||||
padding-left: 20upx;
|
||||
|
||||
&:last-child {
|
||||
justify-content: flex-end;
|
||||
padding-right: 20upx;
|
||||
}
|
||||
|
||||
&.confrim {
|
||||
color: #007AFF;
|
||||
}
|
||||
}
|
||||
|
||||
.t-mask {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background: rgba(0, 0, 0, 0.6);
|
||||
z-index: -1;
|
||||
transition: all 0.3s;
|
||||
opacity: 0;
|
||||
|
||||
&.active {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.t-color__box {
|
||||
position: relative;
|
||||
height: 400upx;
|
||||
background: rgb(255, 0, 0);
|
||||
overflow: hidden;
|
||||
box-sizing: border-box;
|
||||
margin: 0 20upx;
|
||||
margin-top: 20upx;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.t-background {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background: linear-gradient(to right, #fff, rgba(255, 255, 255, 0));
|
||||
}
|
||||
|
||||
.t-color-mask {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
width: 100%;
|
||||
height: 400upx;
|
||||
background: linear-gradient(to top, #000, rgba(0, 0, 0, 0));
|
||||
}
|
||||
|
||||
.t-pointer {
|
||||
position: absolute;
|
||||
bottom: -8px;
|
||||
left: -8px;
|
||||
z-index: 2;
|
||||
width: 15px;
|
||||
height: 15px;
|
||||
border: 1px #fff solid;
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
.t-show-color {
|
||||
width: 100upx;
|
||||
height: 50upx;
|
||||
}
|
||||
|
||||
.t-control__box {
|
||||
margin-top: 50upx;
|
||||
width: 100%;
|
||||
display: flex;
|
||||
padding-left: 20upx;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.t-control__color {
|
||||
flex-shrink: 0;
|
||||
width: 100upx;
|
||||
height: 100upx;
|
||||
border-radius: 50%;
|
||||
background-color: #fff;
|
||||
background-image: linear-gradient(45deg, #eee 25%, transparent 25%, transparent 75%, #eee 75%, #eee),
|
||||
linear-gradient(45deg, #eee 25%, transparent 25%, transparent 75%, #eee 75%, #eee);
|
||||
background-size: 36upx 36upx;
|
||||
background-position: 0 0, 18upx 18upx;
|
||||
border: 1px #eee solid;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.t-control__color-content {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.t-control-box__item {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
width: 100%;
|
||||
padding: 0 30upx;
|
||||
}
|
||||
|
||||
.t-controller {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: 16px;
|
||||
background-color: #fff;
|
||||
background-image: linear-gradient(45deg, #eee 25%, transparent 25%, transparent 75%, #eee 75%, #eee),
|
||||
linear-gradient(45deg, #eee 25%, transparent 25%, transparent 75%, #eee 75%, #eee);
|
||||
background-size: 32upx 32upx;
|
||||
background-position: 0 0, 16upx 16upx;
|
||||
}
|
||||
|
||||
.t-hue {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: linear-gradient(to right, #f00 0%, #ff0 17%, #0f0 33%, #0ff 50%, #00f 67%, #f0f 83%, #f00 100%);
|
||||
}
|
||||
|
||||
.t-transparency {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: linear-gradient(to right, rgba(0, 0, 0, 0) 0%, rgb(0, 0, 0));
|
||||
}
|
||||
|
||||
.t-circle {
|
||||
position: absolute;
|
||||
top: -2px;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
box-sizing: border-box;
|
||||
border-radius: 50%;
|
||||
background: #fff;
|
||||
box-shadow: 0 0 2px 1px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.t-result__box {
|
||||
margin-top: 20upx;
|
||||
padding: 10upx;
|
||||
width: 100%;
|
||||
display: flex;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.t-result__item {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 10upx;
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.t-result__box-input {
|
||||
padding: 10upx 0;
|
||||
width: 100%;
|
||||
font-size: 28upx;
|
||||
box-shadow: 0 0 1px 1px rgba(0, 0, 0, 0.1);
|
||||
color: #999;
|
||||
text-align: center;
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
.t-result__box-text {
|
||||
margin-top: 10upx;
|
||||
font-size: 28upx;
|
||||
line-height: 2;
|
||||
}
|
||||
|
||||
.t-select {
|
||||
flex-shrink: 0;
|
||||
width: 150upx;
|
||||
padding: 0 30upx;
|
||||
|
||||
.t-result__box-input {
|
||||
border-radius: 10upx;
|
||||
border: none;
|
||||
color: #999;
|
||||
box-shadow: 1px 1px 2px 1px rgba(0, 0, 0, 0.1);
|
||||
background: #fff;
|
||||
|
||||
&:active {
|
||||
box-shadow: 0px 0px 1px 0px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.t-alternative {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
width: 100%;
|
||||
padding-right: 10upx;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.t-alternative__item {
|
||||
margin-left: 12upx;
|
||||
margin-top: 10upx;
|
||||
width: 50upx;
|
||||
height: 50upx;
|
||||
border-radius: 10upx;
|
||||
background-color: #fff;
|
||||
background-image: linear-gradient(45deg, #eee 25%, transparent 25%, transparent 75%, #eee 75%, #eee),
|
||||
linear-gradient(45deg, #eee 25%, transparent 25%, transparent 75%, #eee 75%, #eee);
|
||||
background-size: 36upx 36upx;
|
||||
background-position: 0 0, 18upx 18upx;
|
||||
border: 1px #eee solid;
|
||||
overflow: hidden;
|
||||
|
||||
&:active {
|
||||
transition: all 0.3s;
|
||||
transform: scale(1.1);
|
||||
}
|
||||
}
|
||||
|
||||
.t-alternative__item-content {
|
||||
width: 50upx;
|
||||
height: 50upx;
|
||||
background: rgba(255, 0, 0, 0.5);
|
||||
}
|
||||
</style>
|
||||
90
src/components/geek-xd/components/geek-qrcode/README.md
Normal file
90
src/components/geek-xd/components/geek-qrcode/README.md
Normal file
@ -0,0 +1,90 @@
|
||||
# uni-app 二维码生成器
|
||||
|
||||
改自作者诗小柒的tki-qrcode二维码生成器
|
||||
|
||||
### 作者:董玉可
|
||||
|
||||
1. H5、微信小程序、支付宝小程序、APP,其它平台的小程序没有测试
|
||||
2. 使用canvas生成
|
||||
3. 可设置二维码背景色,前景色,角标色
|
||||
4. 可设置二维码logo
|
||||
|
||||
## 重要的事情说3遍 重要的事情说3遍 重要的事情说3遍
|
||||
|
||||
1. IOS、Android真机都可以正常生成二维码
|
||||
2. 使用的时候出现无法生成二维码或空白的请先github直接打包下载,问题依旧,请github上直接提出问题并配图
|
||||
3. 有问题请说明问题原因,这样我才好定位,否则我也无法解决
|
||||
4. 如果此插件有帮助到你请打5分或赞赏我,你的支持是我更新的动力
|
||||
|
||||
+ 图片1 是微信小程序真机实测
|
||||
+ 图片2 是微信小程序模拟实测
|
||||
+ 图片3 是支付宝小程序模拟器实测
|
||||
+ 图片4 是安卓真机实测
|
||||
+ 图片5 H5
|
||||
|
||||
### 使用方法
|
||||
|
||||
在 `template` 中使用
|
||||
|
||||
```javascript
|
||||
<view class="qrimg">
|
||||
<geek-qrcode
|
||||
ref="qrcode"
|
||||
:cid="cid"
|
||||
:val="val"
|
||||
:size="size"
|
||||
:unit="unit"
|
||||
:background="background"
|
||||
:foreground="foreground"
|
||||
:pdground="pdground"
|
||||
:icon="icon"
|
||||
:iconSize="iconsize"
|
||||
:lv="lv"
|
||||
:onval="onval"
|
||||
:loadMake="loadMake"
|
||||
:usingComponents="usingComponents"
|
||||
:showLoading="showLoading"
|
||||
:loadingText="loadingText"
|
||||
@result="qrR" />
|
||||
</view>
|
||||
```
|
||||
|
||||
### 属性
|
||||
|
||||
| 属性名 | 类型 | 默认值 | 可选值 | 说明 |
|
||||
| :-------------- | :-----: | :---------------: | :----: | :-------------------------------------------------------------------------------------------------- |
|
||||
| cid | String | tki-qrcode-canvas | | canvasId,页面存在多个二维码组件时需设置不同的ID |
|
||||
| size | Number | 200 | | 生成的二维码大小 |
|
||||
| unit | String | upx | px | 大小单位尺寸 |
|
||||
| show | Boolean | true | | 默认使用组件中的image标签显示二维码 |
|
||||
| val | String | 二维码 | | 要生成的内容 |
|
||||
| background | String | #000000 | | 二维码背景色 |
|
||||
| foreground | String | #ffffff | | 二维码前景色 |
|
||||
| pdground | String | #ffffff | | 二维码角标色 |
|
||||
| icon | String | | | 二维码图标URL(必须是本地图片,网络图需要先下载至本地) |
|
||||
| iconSize | Number | 40 | | 二维码图标大小(注意此大小不会跟随二维码size 动态变化,设置时需要注意大小,不要太大,以免无法识别) |
|
||||
| lv | Number | 3 | | 容错级别(一般不用设置) |
|
||||
| onval | Boolean | false | | 监听val值变化自动重新生成二维码 |
|
||||
| loadMake | Boolean | false | | 组件初始化完成后自动生成二维码,val需要有值 |
|
||||
| usingComponents | Boolean | true | false | 是否使用了自定义组件模式(主要是为了修复非自定义组件模式时 v-if 无法生成二维码的问题) |
|
||||
| showLoading | Boolean | true | false | 是否显示loading |
|
||||
| loadingText | String | 二维码生成中 | | loading文字 |
|
||||
|
||||
### 方法
|
||||
|
||||
| 方法名 | 参数 | 默认值 | 说明 |
|
||||
| :----------- | :--: | :----: | :-------------------------------------------------- |
|
||||
| _makeCode() | | | 生成二维码 |
|
||||
| _clearCode() | | | 清空二维码(清空二维码会触发result回调 返回值为空) |
|
||||
| _saveCode() | | | 保存二维码到图库 |
|
||||
|
||||
### 事件
|
||||
|
||||
| 事件名 | 返回值 | 说明 |
|
||||
| :----- | :----------------------------: | --------------------------------------: |
|
||||
| result | 生成的图片base64或图片临时地址 | 返回二维码路径 注:_clearCode()后返回空 |
|
||||
|
||||
### 感谢
|
||||
|
||||
[uni-app](https://uniapp.dcloud.io/ "uni-app")
|
||||
[qrcode](https://github.com/aralejs/qrcode "qrcode")
|
||||
205
src/components/geek-xd/components/geek-qrcode/geek-qrcode.vue
Normal file
205
src/components/geek-xd/components/geek-qrcode/geek-qrcode.vue
Normal file
@ -0,0 +1,205 @@
|
||||
<template xlang="wxml" minapp="mpvue">
|
||||
<view class="geek-qrcode">
|
||||
<canvas class="geek-qrcode-canvas" :canvas-id="cid" :style="{width:cpSize+'px',height:cpSize+'px'}" />
|
||||
<image v-show="show" :src="result" :style="{width:cpSize+'px',height:cpSize+'px'}" />
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import QRCode from "./qrcode.js"
|
||||
let qrcode
|
||||
export default {
|
||||
name: "geek-qrcode",
|
||||
props: {
|
||||
cid: {
|
||||
type: String,
|
||||
default: 'geek-qrcode-canvas'
|
||||
},
|
||||
size: {
|
||||
type: Number,
|
||||
default: 200
|
||||
},
|
||||
unit: {
|
||||
type: String,
|
||||
default: 'upx'
|
||||
},
|
||||
show: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
val: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
background: {
|
||||
type: String,
|
||||
default: '#ffffff'
|
||||
},
|
||||
foreground: {
|
||||
type: String,
|
||||
default: '#000000'
|
||||
},
|
||||
pdground: {
|
||||
type: String,
|
||||
default: '#000000'
|
||||
},
|
||||
icon: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
iconSize: {
|
||||
type: Number,
|
||||
default: 40
|
||||
},
|
||||
lv: {
|
||||
type: Number,
|
||||
default: 3
|
||||
},
|
||||
onval: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
loadMake: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
usingComponents: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
showLoading: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
loadingText: {
|
||||
type: String,
|
||||
default: '二维码生成中'
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
result: '',
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
_makeCode() {
|
||||
let that = this
|
||||
if (!this._empty(this.val)) {
|
||||
qrcode = new QRCode({
|
||||
context: that, // 上下文环境
|
||||
canvasId:that.cid, // canvas-id
|
||||
usingComponents: that.usingComponents, // 是否是自定义组件
|
||||
showLoading: that.showLoading, // 是否显示loading
|
||||
loadingText: that.loadingText, // loading文字
|
||||
text: that.val, // 生成内容
|
||||
size: that.cpSize, // 二维码大小
|
||||
background: that.background, // 背景色
|
||||
foreground: that.foreground, // 前景色
|
||||
pdground: that.pdground, // 定位角点颜色
|
||||
correctLevel: that.lv, // 容错级别
|
||||
image: that.icon, // 二维码图标
|
||||
imageSize: that.iconSize,// 二维码图标大小
|
||||
cbResult: function (res) { // 生成二维码的回调
|
||||
that._result(res)
|
||||
},
|
||||
});
|
||||
} else {
|
||||
uni.showToast({
|
||||
title: '二维码内容不能为空',
|
||||
icon: 'none',
|
||||
duration: 2000
|
||||
});
|
||||
}
|
||||
},
|
||||
_clearCode() {
|
||||
this._result('')
|
||||
qrcode.clear()
|
||||
},
|
||||
_saveCode() {
|
||||
let that = this;
|
||||
if (this.result != "") {
|
||||
uni.saveImageToPhotosAlbum({
|
||||
filePath: that.result,
|
||||
success: function () {
|
||||
uni.showToast({
|
||||
title: '二维码保存成功',
|
||||
icon: 'success',
|
||||
duration: 2000
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
_result(res) {
|
||||
this.result = res;
|
||||
this.$emit('result', res)
|
||||
},
|
||||
_empty(v) {
|
||||
let tp = typeof v,
|
||||
rt = false;
|
||||
if (tp == "number" && String(v) == "") {
|
||||
rt = true
|
||||
} else if (tp == "undefined") {
|
||||
rt = true
|
||||
} else if (tp == "object") {
|
||||
if (JSON.stringify(v) == "{}" || JSON.stringify(v) == "[]" || v == null) rt = true
|
||||
} else if (tp == "string") {
|
||||
if (v == "" || v == "undefined" || v == "null" || v == "{}" || v == "[]") rt = true
|
||||
} else if (tp == "function") {
|
||||
rt = false
|
||||
}
|
||||
return rt
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
size: function (n, o) {
|
||||
if (n != o && !this._empty(n)) {
|
||||
this.cSize = n
|
||||
if (!this._empty(this.val)) {
|
||||
setTimeout(() => {
|
||||
this._makeCode()
|
||||
}, 100);
|
||||
}
|
||||
}
|
||||
},
|
||||
val: function (n, o) {
|
||||
if (this.onval) {
|
||||
if (n != o && !this._empty(n)) {
|
||||
setTimeout(() => {
|
||||
this._makeCode()
|
||||
}, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
cpSize() {
|
||||
if(this.unit == "upx"){
|
||||
return uni.upx2px(this.size)
|
||||
}else{
|
||||
return this.size
|
||||
}
|
||||
}
|
||||
},
|
||||
mounted: function () {
|
||||
if (this.loadMake) {
|
||||
if (!this._empty(this.val)) {
|
||||
setTimeout(() => {
|
||||
this._makeCode()
|
||||
}, 0);
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
</script>
|
||||
<style>
|
||||
.geek-qrcode {
|
||||
position: relative;
|
||||
}
|
||||
.geek-qrcode-canvas {
|
||||
position: fixed;
|
||||
top: -99999upx;
|
||||
left: -99999upx;
|
||||
z-index: -99999;
|
||||
}
|
||||
</style>
|
||||
1206
src/components/geek-xd/components/geek-qrcode/qrcode.js
Normal file
1206
src/components/geek-xd/components/geek-qrcode/qrcode.js
Normal file
File diff suppressed because it is too large
Load Diff
@ -232,6 +232,9 @@
|
||||
"pages": [
|
||||
{
|
||||
"path": "index/index"
|
||||
},
|
||||
{
|
||||
"path": "code/index"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@ -9,6 +9,12 @@ export default [
|
||||
title: '组件展示',
|
||||
title_en: 'index',
|
||||
},
|
||||
{
|
||||
path: '/pages_geek/pages/code/index',
|
||||
icon: 'wxCenter',
|
||||
title: '二维码',
|
||||
title_en: 'index',
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
|
||||
130
src/pages_geek/pages/code/index.vue
Normal file
130
src/pages_geek/pages/code/index.vue
Normal file
@ -0,0 +1,130 @@
|
||||
<template xlang="wxml">
|
||||
<view class="container">
|
||||
<view class="qrimg">
|
||||
<view class="qrimg-i">
|
||||
<geek-qrcode v-if="ifShow" cid="qrcode1" ref="qrcode" :val="val" :size="size" :unit="unit"
|
||||
:background="background" :foreground="foreground" :pdground="pdground" :icon="icon" :iconSize="iconsize"
|
||||
:lv="lv" :onval="onval" :loadMake="loadMake" :usingComponents="true" @result="qrR" />
|
||||
</view>
|
||||
<view class="qrimg-i">
|
||||
<geek-qrcode v-if="ifShow" cid="qrcode2" ref="qrcode2" val="第二个二维码" :size="size" :onval="onval"
|
||||
:loadMake="loadMake" :usingComponents="true" @result="qrR" />
|
||||
</view>
|
||||
</view>
|
||||
<view class="uni-padding-wrap">
|
||||
<view class="uni-title">请输入要生成的二维码内容</view>
|
||||
</view>
|
||||
<view class="uni-list">
|
||||
<input class="uni-input" placeholder="请输入要生成的二维码内容" v-model="val" />
|
||||
</view>
|
||||
<view class="uni-padding-wrap uni-common-mt">
|
||||
<view class="uni-title">设置二维码大小</view>
|
||||
</view>
|
||||
<view class="body-view">
|
||||
<slider :value="size" @change="sliderchange" min="50" max="500" show-value />
|
||||
</view>
|
||||
<view class="uni-padding-wrap">
|
||||
<view class="btns">
|
||||
<button type="primary" @tap="selectIcon">选择二维码图标</button>
|
||||
<button type="primary" @tap="creatQrcode">生成二维码</button>
|
||||
<button type="primary" @tap="saveQrcode">保存到图库</button>
|
||||
<button type="warn" @tap="clearQrcode">清除二维码</button>
|
||||
<button type="warn" @tap="ifQrcode">显示隐藏二维码</button>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
<script>
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
ifShow: true,
|
||||
val: '二维码', // 要生成的二维码值
|
||||
size: 200, // 二维码大小
|
||||
unit: 'upx', // 单位
|
||||
background: '#b4e9e2', // 背景色
|
||||
foreground: '#309286', // 前景色
|
||||
pdground: '#32dbc6', // 角标色
|
||||
icon: '', // 二维码图标
|
||||
iconsize: 40, // 二维码图标大小
|
||||
lv: 3, // 二维码容错级别 , 一般不用设置,默认就行
|
||||
onval: false, // val值变化时自动重新生成二维码
|
||||
loadMake: true, // 组件加载完成后自动生成二维码
|
||||
src: '' // 二维码生成后的图片地址或base64
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
sliderchange(e) {
|
||||
this.size = e.detail.value
|
||||
},
|
||||
creatQrcode() {
|
||||
this.$refs.qrcode._makeCode()
|
||||
},
|
||||
saveQrcode() {
|
||||
this.$refs.qrcode._saveCode()
|
||||
},
|
||||
qrR(res) {
|
||||
this.src = res
|
||||
},
|
||||
clearQrcode() {
|
||||
this.$refs.qrcode._clearCode()
|
||||
this.val = ''
|
||||
},
|
||||
ifQrcode() {
|
||||
this.ifShow = !this.ifShow
|
||||
},
|
||||
selectIcon() {
|
||||
let that = this
|
||||
uni.chooseImage({
|
||||
count: 1, //默认9
|
||||
sizeType: ['original', 'compressed'], //可以指定是原图还是压缩图,默认二者都有
|
||||
sourceType: ['album'], //从相册选择
|
||||
success: function (res) {
|
||||
that.icon = res.tempFilePaths[0]
|
||||
setTimeout(() => {
|
||||
that.creatQrcode()
|
||||
}, 100);
|
||||
// console.log(res.tempFilePaths);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
/* @import "../../../common/icon.css"; */
|
||||
.container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.qrimg {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.qrimg-i {
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
slider {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
input {
|
||||
width: 100%;
|
||||
margin-bottom: 20upx;
|
||||
}
|
||||
|
||||
.btns {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
button {
|
||||
width: 100%;
|
||||
margin-top: 10upx;
|
||||
}</style>
|
||||
@ -32,11 +32,27 @@
|
||||
<geek-order v-for="item, index in orderList" :key="index" :img="item.img" :label="item.title" :shop="item.shop"
|
||||
:status="item.status" :price="item.price" @more="modal.msg('更多')" @again="modal.msg('再次购买')" @return="modal.msg('退换')"
|
||||
@sell="modal.msg('卖了换钱')" :num="item.num"></geek-order>
|
||||
|
||||
<uni-section class="mb-10" title="颜色选择器" sub-title="order" type="line"></uni-section>
|
||||
<geek-color-picker ref="gk"></geek-color-picker>
|
||||
<button @click="open()">打开颜色选择器</button>
|
||||
|
||||
|
||||
<uni-section class="mb-10" title="二维码" sub-title="order" type="line"></uni-section>
|
||||
<geek-qrcode cid="qrcode2" ref="qrcode2" val="二维码" :loadMake="true"/>
|
||||
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, reactive } from 'vue';
|
||||
import modal from '@/plugins/modal'
|
||||
const gk = ref(null)
|
||||
function open(){
|
||||
//@ts-ignore
|
||||
gk.value.open()
|
||||
}
|
||||
|
||||
|
||||
|
||||
const menus = reactive([
|
||||
{ icon: "/static/images/icon/rocket.png", label: '抢单' },
|
||||
@ -67,7 +83,6 @@ const commodityList = reactive([
|
||||
}
|
||||
])
|
||||
|
||||
|
||||
const orderList = [
|
||||
{
|
||||
shop: 'geek自营旗舰店',
|
||||
@ -98,6 +113,8 @@ const orderList = [
|
||||
num: 10
|
||||
}
|
||||
]
|
||||
|
||||
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped></style>
|
||||
|
||||
Loading…
Reference in New Issue
Block a user