This commit is contained in:
cansnow
2025-12-17 08:47:58 +08:00
parent 916cb22ecc
commit cf1ad1c24b
68 changed files with 2423 additions and 6104 deletions
+1
View File
@@ -21,6 +21,7 @@ uniCloud-aliyun
/build
/unpackage/cache
/unpackage/debug
/unpackage/release
/unpackage/dist
/unpackage/resources
-1
View File
@@ -21,7 +21,6 @@
"Camera" : {},
"Record" : {},
"Geolocation" : {},
"Maps" : {},
"Fingerprint" : {},
"Contacts" : {},
"Barcode" : {}
+6
View File
@@ -344,6 +344,12 @@
"style": {
"navigationBarTitleText": ""
}
},
{
"path": "pages/workbench/near/near",
"style": {
"navigationBarTitleText": ""
}
}
],
"tabBar": {
+99 -272
View File
@@ -1,42 +1,27 @@
<template>
<view class="map_page">
<uni-nav-bar
left-icon="back"
@clickLeft="back"
statusBar
fixed
backgroundColor="transparent"
>
<u-navbar left-icon="arrow-left" @leftClick="back" placeholder bgColor="transparent">
<template slot="right">
<u-button type="primary" @click="confirm">确定</u-button>
<u-button type="primary" class="confirm_btn" size="mini" @click="confirm">确定</u-button>
</template>
</uni-nav-bar>
<map
ref="map"
class="map_containter"
:latitude="mapCenter.lat"
:longitude="mapCenter.lng"
:scale="scale"
@regionchange="onMapRegionChange"
show-location
>
<!-- 中心点marker -->
<cover-view class="center_marker">
<cover-image src="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='30' height='30' viewBox='0 0 30 30'%3E%3Ccircle cx='15' cy='15' r='12' fill='%23FF4444' opacity='0.8'/%3E%3Ccircle cx='15' cy='15' r='8' fill='%23FFFFFF'/%3E%3C/svg%3E"></cover-image>
</cover-view>
</map>
<!-- 加载提示 -->
<view v-if="isLoading" class="loading_overlay">
<uni-load-more status="loading" content-text="加载中"></uni-load-more>
</view>
<!-- 地址信息展示 -->
<view class="address_info">
<view class="address_title">位置信息</view>
<view class="address_text">{{ currentAddress || '加载中...' }}</view>
<view class="coordinates">
经度: {{ mapCenter.lng.toFixed(6) }} | 纬度: {{ mapCenter.lat.toFixed(6) }}
</u-navbar>
<view style="display: flex; flex-direction: column; height: 100%;">
<!-- 使用web-view嵌入天地图 -->
<ly-map class="ly-map"
v-if="lng && lat"
@onUserEvent="onUserEvent"
ref="map"
:lonlat=[lng,lat]
:map-key="apikey" />
<view class="search_container" v-if="1==2">
<u-search placeholder="日照香炉生紫烟" v-model="keyword"></u-search>
<u-cell-group :customStyle="{backgroundColor:'#FFF'}">
<u-cell title="摇一摇" icon="/static/images/workbench/05.png" :size="cellSize"></u-cell>
<u-cell title="看一看" icon="/static/images/workbench/06.png" :size="cellSize"></u-cell>
<u-cell title="听一听" icon="/static/images/workbench/06.png" :size="cellSize"></u-cell>
<u-cell title="附近" icon="/static/images/workbench/08.png" :size="cellSize"></u-cell>
<u-cell title="购物" icon="/static/images/workbench/09.png" :size="cellSize"></u-cell>
</u-cell-group>
</view>
</view>
</view>
@@ -46,184 +31,104 @@
export default {
data() {
return {
// 地图中心点
mapCenter: {
lng: 116.397128,
lat: 39.916527
},
// 初始中心点(用于比较)
initialCenter: {
lng: 116.397128,
lat: 39.916527
},
// 地图缩放级别
scale: 16,
// 当前地址
currentAddress: '北京市朝阳区',
// 选中的位置数据
selectedPoint: null,
// 是否正在加载
isLoading: false,
// 地图对象
mapContext: null,
// 地址解析延迟器
addressTimer: null,
// 高德地图API key(需要替换为实际的key)
aMapKey: ''
keyword:"",
cellSize:"base",
apikey:"ecc44b16c51c888d625f0238d678a61b",
type:"chooselocation",
lng:"",
lat:"",
address:"",
}
},
mounted() {
this.initMap();
},
beforeDestroy() {
// 清理定时器
if (this.addressTimer) {
clearTimeout(this.addressTimer);
async onLoad(opt) {
if(opt.type){
this.type = opt.type;
}
if(opt.lng){
this.lng = opt.lng;
}
if(opt.lat){
this.lat = opt.lat;
}
if(opt.address){
this.address = opt.address;
}
this.init();
},
methods: {
/**
* 初始化地图
*/
initMap() {
this.mapContext = uni.createMapContext('map', this);
// 获取当前位置权限
this.getCurrentLocation();
},
/**
* 获取当前位置
*/
getCurrentLocation() {
uni.getLocation({
type: 'gcj02',
success: (res) => {
this.mapCenter = {
lng: res.longitude,
lat: res.latitude
};
this.initialCenter = {
lng: res.longitude,
lat: res.latitude
};
// 获取地址
this.getAddressFromCoordinates(res.longitude, res.latitude);
},
fail: (err) => {
// 权限拒绝或失败,使用默认位置
console.log('获取位置失败,使用默认位置', err);
this.getAddressFromCoordinates(this.mapCenter.lng, this.mapCenter.lat);
}
});
},
/**
* 地图区域变化时触发
*/
onMapRegionChange(e) {
if (e.type === 'end') {
// 获取地图中心点坐标
this.mapContext.getCenterLocation({
success: (res) => {
this.mapCenter = {
lng: res.longitude,
lat: res.latitude
};
// 延迟获取地址,避免频繁调用
this.debounceGetAddress();
// 初始化
init() {
const _this = this;
if(this.type=='chooselocation'){
uni.getLocation({
success(res) {
_this.lng = res.longitude;
_this.lat = res.latitude;
},
fail: (err) => {
console.log('获取地图中心点失败', err);
fail() {
_this.lng = 113;
_this.lat = 40;
}
});
})
}
},
/**
* 防抖获取地址
*/
debounceGetAddress() {
if (this.addressTimer) {
clearTimeout(this.addressTimer);
onUserEvent(e) {
//console.log(e)
if(this.type=='chooselocation'){
if(e.type == "move"){
this.lng = e.lng;
this.lat = e.lat;
this.$refs.map.setMarkers([
{
lon: Number(e.lng),
lat: Number(e.lat)
}
]);
}
}
if(this.type=='viewlocation'){
}
this.addressTimer = setTimeout(() => {
this.getAddressFromCoordinates(this.mapCenter.lng, this.mapCenter.lat);
}, 500);
},
/**
* 根据坐标获取地址(使用高德地图API)
*/
getAddressFromCoordinates(lng, lat) {
// 方法1:使用高德地图逆地理编码API
// 注意:需要在高德地图申请API key
const aMapUrl = `https://restapi.amap.com/v3/geocode/regeo?location=${lng},${lat}&key=YOUR_AMAP_KEY`;
// 这里使用本地模拟,实际项目中应该调用真实API
this.simulateAddressLookup(lng, lat);
},
/**
* 模拟地址查询(实际项目应调用真实的地理编码API)
*/
simulateAddressLookup(lng, lat) {
// 简单的地址模拟逻辑
const addresses = [
'北京市朝阳区建国路88号',
' 北京市东城区天安门广场',
'上海市浦东新区陆家嘴环路1088号',
'深圳市福田区中心区',
'杭州市上城区武林路'
];
// 根据坐标返回对应的地址(这里是随机模拟)
const index = Math.floor((lng + lat) % addresses.length);
this.currentAddress = addresses[index] || `经度: ${lng.toFixed(6)}, 纬度: ${lat.toFixed(6)}`;
},
/**
* 确定位置按钮点击
*/
confirm() {
const _this = this;
// 验证是否有有效的位置数据
if (!this.mapCenter.lng || !this.mapCenter.lat) {
if (!this.lng || !this.lat) {
uni.showToast({
title: '位置数据无效',
title: '请选择位置',
icon: 'none'
});
return;
}
// 保存选中的位置
_this.selectedPoint = {
lng: this.mapCenter.lng,
lat: this.mapCenter.lat,
address: this.currentAddress
};
// 通过事件通道返回数据给父页面
const eventChannel = this.getOpenerEventChannel();
eventChannel.emit('onConfirm', {
lng: this.mapCenter.lng,
lat: this.mapCenter.lat,
address: this.currentAddress
const url=`http://api.tianditu.gov.cn/geocoder?postStr={"lon":${this.lng},"lat":${this.lat},"ver":1}&type=geocode&tk=${this.apikey}`;
console.log(url);
uni.request({
url:url,
success(res){
//console.log(res.data);
const result = res.data.result;
_this.address = result.formatted_address;
console.log( {
lng: result.location.lon,
lat: result.location.lat,
address: result.formatted_address
});
//return 1;
// 通过事件通道返回数据给父页面
const eventChannel = _this.getOpenerEventChannel();
eventChannel.emit('onConfirm', {
lng: result.location.lon,
lat: result.location.lat,
address: result.formatted_address
});
uni.navigateBack();
},
});
// 显示成功提示
uni.showToast({
title: '位置已确定',
icon: 'success',
duration: 1500
});
// 延迟返回,让用户看到提示
setTimeout(() => {
uni.navigateBack();
}, 1500);
return 1;
},
/**
* 返回上一页
*/
@@ -243,91 +148,13 @@
background: #fff;
position: relative;
overflow: hidden;
.map_containter {
.map_container {
flex: 1;
width: 100%;
height: 100%;
}
// 中心点marker样式
.center_marker {
position: absolute;
top: 50%;
left: 50%;
width: 30px;
height: 30px;
margin-left: -15px;
margin-top: -15px;
z-index: 100;
pointer-events: none;
cover-image {
width: 100%;
height: 100%;
animation: bounce 1.5s ease-in-out infinite;
}
}
// 加载覆盖层
.loading_overlay {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
display: flex;
align-items: center;
justify-content: center;
background: rgba(0, 0, 0, 0.3);
z-index: 200;
}
// 地址信息展示框
.address_info {
position: absolute;
bottom: 0;
left: 0;
right: 0;
background: rgba(255, 255, 255, 0.95);
backdrop-filter: blur(10px);
padding: 12px 16px;
z-index: 99;
border-top: 1px solid #e5e5e5;
box-shadow: 0 -2px 10px rgba(0, 0, 0, 0.1);
.address_title {
font-size: 12px;
color: #999;
margin-bottom: 6px;
}
.address_text {
color: #333;
font-size: 14px;
line-height: 1.5;
word-break: break-word;
margin-bottom: 6px;
font-weight: 500;
}
.coordinates {
color: #666;
font-size: 12px;
font-family: monospace;
}
}
}
// 跳动动画
@keyframes bounce {
0%, 100% {
transform: scale(1);
opacity: 1;
}
50% {
transform: scale(1.2);
opacity: 0.8;
}
.confirm_btn{
padding: 30rpx 10rpx;
}
</style>
@@ -0,0 +1,176 @@
<template>
<!-- #ifdef APP-PLUS -->
<view class="voice_title" @touchstart.stop.prevent="startVoice"
@touchmove.stop.prevent="moveVoice" @touchend.stop="endVoice"
@touchcancel.stop="cancelVoice" :style="{ background: recording ? '#c7c6c6' : '#FFFFFF' }">
<text>{{ voiceTitle }}</text>
</view>
<!-- #endif -->
</template>
<script>
import {mapActions,mapGetters} from "vuex";
export default {
name:"Recoder",
props: {
placeholder: {
type: String,
default: "",
},
maxlength: {
type: Number,
default: -1,
}
},
data() {
return {
recording:false,
sendMsgTimmer: null, //发送时间定时器
sendDuring: 0, //发送时间计数器 1分钟以内的信息不显示时间
sendTimeBetween: 60, //发送信息显示的间隔,60秒以内信息不显示发送时间
voiceTitle:"点击录音",
AudioExam:null,
isStopVoice : false,
voiceCanSend : true,
voiceIconText : "正在录音...",
PointX : 0,
PointY : 0,
voiceInterval:null,
voiceTime:0,
};
},
watch: {
voiceIconText(nv,ov){
this.$emit('RecodeEvent',{type:"voiceIconTextChange",text:nv})
},
recording(nv,ov){
this.$emit('RecodeEvent',{type:"recordingStateChange",state:nv})
}
},
created() {
const _this = this;
//录音器,getRecorderManager不支持H5
// #ifdef APP-PLUS
//获取全局唯一的录音管理器(https://uniapp.dcloud.net.cn/api/media/record-manager.html#getrecordermanager
_this.Recorder = uni.getRecorderManager();
//录音开始事件
_this.Recorder.onStart(e => {
_this.beginVoice();
});
//录音结束事件
_this.Recorder.onStop(res => {
clearInterval(_this.voiceInterval);
_this.handleRecorder(res);
});
// #endif
},
methods: {
...mapActions("message", ["updateCurrentMsg"]),
/*----------------------------------------------------H5不支持)录音相关 start-------------------------------------- */
//准备开始录音
startVoice(e) {
//如果音频正在播放 先暂停。
this.updateCurrentMsg={clientMsgID:""};
this.recording = true;
this.isStopVoice = false;
this.voiceCanSend = true;
this.voiceIconText = "正在录音..."
this.PointY = e.touches[0].clientY;
this.Recorder.start({
format: 'mp3'
});
},
//录音已经开始
beginVoice() {
let that = this;
if (that.isStopVoice) {
that.Recorder.stop();
return;
}
that.voiceTitle = '松开 结束'
that.voiceInterval = setInterval(() => {
console.log("that.voiceTime", that.voiceTime);
if (that.voiceTime > 49) {
that.voiceIconText = "录音结束倒计时[" + (60 - that.voiceTime) + "]s";
};
if (that.voiceTime == 60) {
clearInterval(that.voiceInterval);
that.endVoice();
}
that.voiceTime++;
}, 1000)
},
//move 正在录音中
moveVoice(e) {
const PointY = e.touches[0].clientY;
const slideY = this.PointY - PointY;
if (slideY > uni.upx2px(120)) {
this.voiceCanSend = false;
this.voiceIconText = '松开手指 取消发送 '
} else if (slideY > uni.upx2px(60)) {
this.voiceCanSend = true;
this.voiceIconText = '手指上滑 取消发送 '
} else {
this.voiceIconText = '正在录音... '
}
},
//结束录音
endVoice() {
this.isStopVoice = true; //加锁 确保已经结束录音并不会录制
this.Recorder.stop();
this.voiceTitle = '按住 说话'
},
//录音被打断
cancelVoice(e) {
console.log("路由被打断", e);
this.voiceTime = 0;
this.voiceTitle = '按住 说话';
this.voiceCanSend = false;
this.Recorder.stop();
},
//处理录音文件
handleRecorder({tempFilePath,duration }) {
if (this.voiceTime < 1) {
this.voiceIconText = "说话时间过短";
setTimeout(() => {
this.recording = false;
}, 500)
return;
}
let contentDuration = this.voiceTime;
this.voiceTime = 0;
this.recording = false;
clearInterval(this.voiceInterval);
//console.log("录音文件", tempFilePath);
//console.log("是否发送语音信息", this.voiceCanSend);
let voiceFile = {
tempFilePath: tempFilePath,
contentDuration: Math.ceil(contentDuration),
anmitionPlay: false,
};
if (this.voiceCanSend) {
//console.log("=====上传语音文件,并发送语音信息====");
this.$emit('RecodeEvent',{type:"sendVoiceMessage",audio:voiceFile})
return;
} else {
console.log("=====已经取消发送语音信息====")
return;
}
}
/*-------------------------------------录音相关方法块 end---------------------------------------------------*/
},
};
</script>
<style lang="scss" scoped>
.voice_title {
text-align: center;
background-color: #ffffff;
height: 70rpx;
line-height: 70rpx;
border-radius: 12rpx;
font-weight: bold;
font-size: 32rpx;
}
</style>
@@ -114,10 +114,6 @@
@include vCenterBox();
flex-direction: column;
&_single {
margin-bottom: 24rpx;
}
.conversation_info {
flex-direction: row;
justify-content: center;
@@ -126,7 +122,6 @@
.title {
@include nomalEllipsis();
max-width: 280rpx;
font-size: 14px;
font-weight: 500;
}
@@ -1,10 +1,13 @@
<template>
<view class="at_text_message_container bg_container">
消息
<u-parse :content="getContent" :previewImg="false" :showImgMenu="false" selectable></u-parse>
</view>
</template>
<script>
import {
parseBr
} from "@/util/common";
export default {
name: "AtTextMessageRender",
components: {},
@@ -12,6 +15,11 @@
message: Object,
conversationID:String,
},
computed: {
getContent() {
return parseBr(this.message.textElem?.content);
},
},
data() {
return {};
},
@@ -1,10 +1,34 @@
<template>
<view class="location_message_container bg_container">
消息
<u--image
:showLoading="true"
:width="selfWidth"
:height="maxHeight"
mode="widthFix"
v-if="src"
:src="src"
@load="onLoaded"
@click="clickMediaItem">
<template v-slot:loading>
<u-loading-icon color="red"></u-loading-icon>
</template>
</u--image>
<u--image
v-else
:showLoading="true"
:width="selfWidth"
mode="widthFix"
src="/static/images/sync_error.png">
</u--image>
<u--text class="address" :style="{
width:selfWidth+'px'
}" :lines="1" :size="20" :text="desc"></u--text>
</view>
</template>
<script>
import util from "@/util"
import md5 from "md5";
export default {
name: "LocationMessageRender",
components: {},
@@ -13,12 +37,85 @@
conversationID:String,
},
data() {
return {};
return {
selfWidth:200,
loadingWidth: "200px",
src:"",
coverDownloading:false,
coverDownloadProgress:"",
apisrc:"",
maxHeight:"",
desc:""
};
},
created() {
let loc = this.message.locationElem;
this.desc = loc.description;
this.apisrc = "http://api.tianditu.gov.cn/staticimage?"
+"center="+loc.longitude+","+loc.latitude
+"&width=400"
+"&height=300"
+"&zoom=10"
+"&markers="+loc.longitude+","+loc.latitude
+"&tk=5255a4be64441ba9fa2ebe605ca472bf";
//
//this.apisrc="http://192.168.1.222/staticimage.png";
this.init();
},
methods: {
getImageInfo(src){
const _this = this;
uni.getImageInfo({
src:src,
success(res) {
const imageHeight = res.height;
const imageWidth = res.width;
const aspectRatio = imageHeight / imageWidth;
_this.maxHeight = (_this.selfWidth * aspectRatio);
console.log(res)
_this.src = src;
}
})
},
async init(){
const self = this;
let url = "";
// 如果有远程 snapshotUrl,则下载到 coverCachePath
const snapshotUrl = this.apisrc ;
const key = md5(snapshotUrl || '');
const dir_name = `${this.conversationID}`;
const save_file_name = `loc_${key}.png`;
const coverCachePath = `${dir_name}/${save_file_name}`;
if (typeof plus === 'undefined' || !coverCachePath) return;
self.coverDownloading = true;
util.cacheFile(snapshotUrl,coverCachePath,(fn)=>{
self.coverDownloading = false;
self.getImageInfo(fn);
console.log(fn);
},(e)=>{
console.log(e);
},(e)=>{
console.log(e);
});
},
clickMediaItem() {
let loc = this.message.locationElem;
uni.navigateTo({
url:"/pages/common/map?type=viewlocation&lng="+loc.longitude+"&lat="+loc.latitude+"&address="+loc.description
})
},
onLoaded() {
this.loadingWidth = "auto";
},
},
};
</script>
<style lang="scss" scoped></style>
<style lang="scss" scoped>
.location_message_container{
.address{
margin-top: 20rpx;
}
}
</style>
@@ -13,6 +13,13 @@
<u-loading-icon color="red"></u-loading-icon>
</template>
</u--image>
<u--image
v-else
:showLoading="true"
width="120"
mode="widthFix"
src="/static/images/sync_error.png">
</u--image>
</view>
</template>
@@ -54,41 +61,22 @@
// 如果有远程 snapshotUrl,则下载到 coverCachePath
const snapshotUrl = (this.message.pictureElem.snapshotPicture?.url ?? this.message.pictureElem.sourcePath );
const key = md5(snapshotUrl || '');
this.coverCachePath = `_doc/${this.conversationID}/img_${key}.jpg`;
if (typeof plus === 'undefined' || !this.coverCachePath) return;
try {
// 检查封面是否存在
const coverExists = await util.fileExsit(self.coverCachePath);
this.coverExists = !!coverExists;
if (this.coverExists) {
this.src = this.coverCachePath;
return;
}
if (!snapshotUrl) {
this.src="/static/images/sync_error.png";
return;
}
this.coverDownloading = true;
await new Promise((resolve, reject) => {
util.downloadFile(snapshotUrl, self.coverCachePath, function(localPath) {
self.coverDownloading = false;
self.coverExists = true;
resolve(localPath);
}, function(err) {
self.coverDownloading = false;
reject(err);
}, function(progress) {
self.coverDownloadProgress = progress;
});
});
} catch (e) {
this.coverDownloading = false;
}
this.coverCachePath = `${this.conversationID}/img_${key}.jpg`;
util.cacheFile(snapshotUrl,this.coverCachePath,(e)=>{
self.coverDownloading = false;
self.src = coverCachePath;
console.log(e);
},(e)=>{
console.log(e);
},(e)=>{
console.log(e);
});
},
clickMediaItem() {
uni.previewImage({
current: 0,
urls: [this.message.pictureElem.sourcePicture.url],
//urls: [this.message.pictureElem.sourcePicture.url],
urls: ["_doc/"+this.coverCachePath],
indicator: "none",
});
},
@@ -104,21 +92,5 @@
position: relative;
border-radius: 16rpx;
overflow: hidden;
.play_icon {
width: 48px;
height: 48px;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
.video_duration {
position: absolute;
bottom: 12rpx;
right: 24rpx;
color: #fff;
}
}
</style>
@@ -1,6 +1,6 @@
<template>
<view class="text_message_container bg_container">
<mp-html :previewImg="false" :showImgMenu="false" :lazyLoad="false" selectable :content="getContent" />
<u-parse :content="getContent" :previewImg="false" :showImgMenu="false" selectable></u-parse>
</view>
</template>
@@ -96,9 +96,7 @@
coverDownloadProgress: 0,
videoDownloading: false,
videoDownloadProgress: 0,
coverExists: false,
videoExists: false,
coverCachePath:"",
videoCachePath:"",
previewVideoFlag: false,
previewVideoSrc: "",
@@ -121,44 +119,19 @@
// 如果有远程 snapshotUrl,则下载到 coverCachePath
const snapshotUrl = this.message?.videoElem?.snapshotUrl;
const key = md5(this.message?.videoElem?.videoUrl || '');
this.coverCachePath = `_doc/${this.conversationID}/cover_${key}.jpg`;
this.videoCachePath = `_doc/${this.conversationID}/${key}.mp4`;
const coverExists = await util.fileExsit(this.coverCachePath);
// 自动缓存封面到 this.coverCachePath
const self = this;
if (typeof plus === 'undefined' || !this.coverCachePath) return;
try {
// 检查封面是否存在
const coverExists = await util.fileExsit(self.coverCachePath);
this.coverExists = !!coverExists;
// 同时检查视频缓存是否存在
const vidExists = await util.fileExsit(self.videoCachePath);
this.videoExists = !!vidExists;
if (this.coverExists) {
this.src = this.coverCachePath;
return;
}
if (!snapshotUrl) {
this.src="/static/images/sync_error.png";
return;
}
this.coverDownloading = true;
await new Promise((resolve, reject) => {
util.downloadFile(snapshotUrl, self.coverCachePath, function(localPath) {
self.coverDownloading = false;
self.coverExists = true;
resolve(localPath);
}, function(err) {
self.coverDownloading = false;
reject(err);
}, function(progress) {
self.coverDownloadProgress = progress;
});
});
} catch (e) {
this.coverDownloading = false;
}
const coverCachePath = `${this.conversationID}/cover_${key}.jpg`;
this.videoCachePath = `${this.conversationID}/${key}.mp4`;
this.videoExists = await util.fileExists(this.videoCachePath);
self.coverDownloading = true;
util.cacheFile(snapshotUrl,coverCachePath,(e)=>{
self.coverDownloading = false;
self.src = coverCachePath;
console.log(e);
},(e)=>{
console.log(e);
},(e)=>{
console.log(e);
});
},
clickMediaItem() {
uni.previewImage({
@@ -173,7 +146,7 @@
onOverlayClick() {
// 点击覆盖层:如果视频已缓存则直接播放,否则开始下载
if (this.videoExists) {
this.playVideo(this.videoCachePath);
this.playVideo("_doc/"+this.videoCachePath);
return;
}
const url = this.message?.videoElem?.videoUrl || this.message?.videoElem?.videoPath;
@@ -186,7 +159,7 @@
util.downloadFile(url, this.videoCachePath, (localPath) => {
this.videoDownloading = false;
this.videoExists = true;
this.playVideo(localPath);
this.playVideo("_doc/"+localPath);
}, (err) => {
this.videoDownloading = false;
uni.showToast({ title: '下载失败' });
@@ -1,24 +1,144 @@
<template>
<view class="voice_message_container bg_container">
消息
<view @tap="handleAudio"
:class="{'content-audio-container':true,'chat-audio-container-me':true}"
:style="'width:'+((message.soundElem.duration)*2+130)+'rpx'">
<view class="voice_icon"
:class="[
{ voice_icon_right: isSender },
{ voice_icon_left: !isSender },
{ voice_icon_right_an: isSender && message.clientMsgID == storeCurrentMsg.clientMsgID},
{ voice_icon_left_an: !isSender && message.clientMsgID == storeCurrentMsg.clientMsgID }
]">
</view>
<view class="">{{message.soundElem.duration}}s</view>
</view>
</view>
</template>v
<script>
import {mapGetters,mapActions} from "vuex";
import util from "@/util"
import md5 from "md5";
export default {
name: "VoiceMessageRender",
components: {},
props: {
message: Object,
isSender: {
type: Boolean,
default: false,
},
conversationID:String,
},
data() {
return {};
console.log(this.message);
return {
src:"",
};
},
computed:{
...mapGetters(["storeCurrentMsg"]),
},
created() {
this.init();
},
methods: {
async init(){
console.log(this.message);
const self = this;
let audio = this.message.soundElem;
//soundPath
// 如果有远程 snapshotUrl,则下载到 cachePath
const snapshotUrl = audio.sourceUrl;
const key = md5(snapshotUrl || '');
const save_file_name = `audio_${key}.${audio.soundType}`;
const cachePath = `${this.conversationID}/${save_file_name}`;
if (typeof plus === 'undefined' || !cachePath) return;
util.cacheFile(snapshotUrl,cachePath,(fn)=>{
self.src = fn;
console.log(fn);
},(e)=>{
console.log(e);
},(e)=>{
console.log(e);
});
},
handleAudio(){
const event = {
message:this.message,
src:this.src,
type:'audio_msg_click'
};
this.$emit("messageEvent",event);
}
},
};
</script>
<style lang="scss" scoped></style>
<style lang="scss" scoped>
//语音信息样式
.content-audio-container {
border-radius: 10rpx;
padding: 0 20rpx;
/* #ifndef APP-NVUE */
max-width: 500rpx;
/* #endif */
display: flex;
flex-direction: row;
align-items: center;
.voice_icon {
height: 34rpx;
width: 34rpx;
background-repeat: no-repeat;
background-size: 100%;
}
.voice_icon_right {
background-image: url('@/static/images/chat/voice/voice-left-3.png');
margin-left: 10rpx;
}
.voice_icon_left {
background-image: url('@/static/images/chat/voice/voice-right-3.png');
margin-right: 10rpx;
}
.voice_icon_right_an {
animation: voiceAn_right 1s linear alternate infinite;
}
.voice_icon_left_an {
animation: voiceAn_left 1s linear alternate infinite;
}
@keyframes voiceAn_right {
0% {
background-image: url('@/static/images/chat/voice/voice-left-1.png');
}
50% {
background-image: url('@/static/images/chat/voice/voice-left-2.png');
}
100% {
background-image: url('@/static/images/chat/voice/voice-left-3.png');
}
}
@keyframes voiceAn_left {
0% {
background-image: url('@/static/images/chat/voice/voice-right-1.png');
}
50% {
background-image: url('@/static/images/chat/voice/voice-right-2.png');
}
100% {
background-image: url('@/static/images/chat/voice/voice-right-3.png');
}
}
}
</style>
@@ -16,7 +16,12 @@
</view>
</view>
<view class="message_content_wrap message_content_wrap_shadow" :id="`message_content_wrap_${source.clientMsgID}`" @longtap.stop.prevent="longtapEvent($event)">
<component :is="component" :message="source" :conversationID="conversationID"></component>
<component :is="component"
@messageEvent="onMessageEvent"
:isSender="isSender"
:message="source"
:conversationID="conversationID"
></component>
</view>
</view>
</view>
@@ -172,7 +177,9 @@
console.log('longtapEvent');
this.$emit('userEvent',{type:"longtapMsgContent"},this.source);
},
onMessageEvent(e){
this.$emit('userEvent',e);
},
},
};
</script>
@@ -295,7 +302,7 @@
flex-direction: row-reverse;
.bg_container {
border-radius: 12rpx 0 12rpx 12rpx;
border-radius: 6rpx;
background-color: #94ec68 !important;
}
}
+58 -10
View File
@@ -44,14 +44,47 @@
selectFlag: false,
selectItems: [],
forwardItems: [],
forwardMerge: false
forwardMerge: false,
Audio:null
};
},
computed: {
...mapGetters([
"storeCurrentConversation",
"storeCurrentConversation","storeCurrentMsg",'storeCurrentMsgID'
]),
},
created() {
this.Audio = uni.createInnerAudioContext(); //音频
/*
设置音频播放模式,可取值:
"ambient" - 不中止其他声音播放,不能后台播放,静音后无声音;
"soloAmbient" - 中止其他声音播放,不能后台播放,静音后无声音;
"playback" - 中止其他声音,可以后台播放,静音后有声音。
默认值为"playback"。
*/
this.Audio.sessionCategory="soloAmbient";
this.Audio.onError((r)=>{
console.log(r);
this.updateCurrentMsg({});
})
this.Audio.onEnded((r)=>{
console.log(r);
this.updateCurrentMsg({});
})
},
watch:{
storeCurrentMsgID(nv,ov){
if(nv!=ov && nv){
if(this.storeCurrentMsg.soundElem.soundPath){
this.Audio.src= this.storeCurrentMsg.soundElem.soundPath;
this.Audio.play();
return ;
}
this.updateCurrentMsg({});
}
console.log(nv,ov,this.Audio,this.storeCurrentMsg);
}
},
onLoad(options) {
//console.log("onload");
this.setPageListener();
@@ -62,16 +95,25 @@
onUnload() {
//console.log("unload");
this.disposePageListener();
markConversationAsRead({
...this.$store.getters.storeCurrentConversation,
},
true
);
markConversationAsRead({...this.$store.getters.storeCurrentConversation,},true);
this.resetConversationState();
this.resetMessageState();
// #ifdef APP-PLUS
if (this.Audio) {
this.Audio.stop();
this.Audio.destroy();
}
// #endif
},
onHide() {
// #ifdef APP-PLUS
if (this.Audio) {
this.Audio.stop();
}
// #endif
},
methods: {
...mapActions("message", ["resetMessageState", "deleteMessages", "pushNewMessage", "updateOneMessage"]),
...mapActions("message", ["updateCurrentMsg","resetMessageState", "deleteMessages", "pushNewMessage", "updateOneMessage"]),
...mapActions("conversation", ["resetConversationState"]),
scrollToBottom(isRecv = false) {
this.$refs.chatingListRef.scrollToBottom(false, isRecv);
@@ -177,7 +219,6 @@
if (!founded) {
arr.push(data);
}
console.log(arr.length);
this.selectItems = [...arr];
}
return;
@@ -258,7 +299,14 @@
})
return;
}
if(e.type == 'audio_msg_click'){
if(_this.storeCurrentMsgID){
_this.updateCurrentMsg({clientMsgID:""});
}else{
_this.updateCurrentMsg(e.message);
}
return ;
}
if (e.type == 'longtapMsgContent') {
let menu = [];
if (data.contentType == MessageType.TextMessage) {
@@ -81,10 +81,10 @@
// }
// }
// })
uni.switchTab({
url:"/pages/profile/index/index"
})
//prepareConversationState(this.storeConversationList[0]);
// uni.switchTab({
// url:"/pages/profile/index/index"
// })
prepareConversationState(this.storeConversationList[1]);
},1000)
},
methods: {
+2 -3
View File
@@ -11,9 +11,9 @@
<image style="width: 46px; height: 46px" src="@/static/images/single_setting_add.png" alt="" />
</view>
</view>
<u-gap></u-gap>
<uni-list>
<uni-list-item title="查找聊天内容" to=""></uni-list-item>
<uni-list-item title="查找聊天内容" :to="'/pages/common/search/index?type=conversation&conversationID='+storeCurrentConversation.conversationID" showArrow></uni-list-item>
</uni-list>
<u-gap></u-gap>
<uni-list>
@@ -103,7 +103,6 @@
}
.setting_row {
margin: 24rpx 24rpx 0 24rpx;
background: #fff;
border-radius: 6px;
overflow: hidden;
+16 -2
View File
@@ -23,9 +23,22 @@
<uni-list-item title="摇一摇" thumb="/static/images/workbench/05.png" :thumbSize="thumbSize" to="/pages/workbench/friend-circle/friend-circle" showArrow></uni-list-item>
<uni-list-item title="看一看" thumb="/static/images/workbench/06.png" :thumbSize="thumbSize" to="/pages/workbench/friend-circle/friend-circle" showArrow></uni-list-item>
<uni-list-item title="听一听" thumb="/static/images/workbench/06.png" :thumbSize="thumbSize" to="/pages/workbench/friend-circle/friend-circle" showArrow></uni-list-item>
<uni-list-item title="附近" thumb="/static/images/workbench/08.png" :thumbSize="thumbSize" to="/pages/workbench/friend-circle/friend-circle" showArrow></uni-list-item>
<uni-list-item title="附近" thumb="/static/images/workbench/08.png" :thumbSize="thumbSize" to="/pages/workbench/near/near" showArrow></uni-list-item>
<uni-list-item title="购物" thumb="/static/images/workbench/09.png" :thumbSize="thumbSize" to="/pages/workbench/friend-circle/friend-circle" showArrow></uni-list-item>
</uni-list>
<u-cell-group :customStyle="{backgroundColor:'#FFF'}">
<u-cell
title="扫一扫"
icon="/static/images/workbench/03.png" :size="cellSize"
isLink
@click="scan"
></u-cell>
<u-cell title="摇一摇" icon="/static/images/workbench/05.png" :size="cellSize" url="/pages/workbench/friend-circle/friend-circle" isLink></u-cell>
<u-cell title="看一看" icon="/static/images/workbench/06.png" :size="cellSize" url="/pages/workbench/friend-circle/friend-circle" isLink></u-cell>
<u-cell title="听一听" icon="/static/images/workbench/06.png" :size="cellSize" url="/pages/workbench/friend-circle/friend-circle" isLink></u-cell>
<u-cell title="附近" icon="/static/images/workbench/08.png" :size="cellSize" url="/pages/workbench/near/near" isLink></u-cell>
<u-cell title="购物" icon="/static/images/workbench/09.png" :size="cellSize" url="/pages/workbench/friend-circle/friend-circle" isLink></u-cell>
</u-cell-group>
</view>
</template>
@@ -33,7 +46,8 @@
export default {
data() {
return {
thumbSize:"base"
thumbSize:"base",
cellSize:"large"
};
},
computed:{
@@ -0,0 +1,86 @@
<template>
<view class="near-item" :index="index">
<view @tap="linkToBusinessCard(item.user_id)">
<MyAvatar :src="item.user.avatar" :desc="item.user.nickname || item.user.id" :isGroup="false" size="48"></MyAvatar>
</view>
<view class="content">
<view class="content-name" @tap="linkToBusinessCard(item.user_id)">{{ item.user.nickname || item.user.remark }}</view>
<view class="content-desc">
<u--text :lines="5" :text="item.body" />
</view>
</view>
</view>
</template>
<script>
import MyAvatar from "@/components/MyAvatar/index.vue";
import util from "@/util/index.js";
export default{
components:{MyAvatar},
props:{
index:{
type:Number,
default:0
},
item:{
type:Object,
default(){
return {};
}
}
},
computed:{
selfInfo() {
return this.$store.getters.storeSelfInfo;
},
},
data(){
//console.log(this.item);
return {
girdItemCustomStyle:{
padding: '0',
margin:'0',
border:'1rpx solid #f2f6fc'
},
}
},
methods:{
linkToBusinessCard(userID){
this.$emit('userEvent',{type:'linkToBusinessCard',userID});
},
cdn:util.cdn
}
}
</script>
<style scoped lang="scss">
.near-item {
padding: 18rpx 30rpx;
border-bottom: 1rpx solid #f2eeee;
display: flex;
flex-direction: row;
justify-content: flex-start;
align-items: flex-start;
.content {
flex: 1;
padding-left: 18rpx;
font-size: 32rpx;
&-name {
color: #666666;
font-weight: bold;
font-size: 32rpx;
}
&-desc {
color: #999999;
padding-top: 4rpx;
//line-height: 36rpx;
font-size: 32rpx;
}
}
}
</style>
+95
View File
@@ -0,0 +1,95 @@
<template>
<view class="near_page">
<u-navbar
@leftClick="leftClick"
@rightClick="moreOptionClick"
leftIcon="arrow-left"
rightIcon="more-dot-fill"
placeholder
bgColor="#FFF"
title="附近的人"
title-size="36"
:title-bold="true"
:border-bottom="false">
</u-navbar>
<view>
<UserItem v-for="(item,index) in list" :item="item" :index="index" :key="index"></UserItem>
</view>
</view>
</template>
<script>
import UserItem from './components/UserItem';
export default {
components:{UserItem},
data() {
return {
list:[
{
user:{
avatar:"/static/img/avatar.png",
id:1,
nickname:"nickname",
remark:"remark"
},
user_id:1,
body:"body"
},
{
user:{
avatar:"/static/img/avatar.png",
id:1,
nickname:"nickname",
remark:"remark"
},
user_id:1,
body:"body"
},
{
user:{
avatar:"/static/img/avatar.png",
id:1,
nickname:"nickname",
remark:"remark"
},
user_id:1,
body:"body"
},
{
user:{
avatar:"/static/img/avatar.png",
id:1,
nickname:"nickname",
remark:"remark"
},
user_id:1,
body:"body"
},
{
user:{
avatar:"/static/img/avatar.png",
id:1,
nickname:"nickname",
remark:"remark"
},
user_id:1,
body:"body"
},
]
};
},
methods:{
moreOptionClick(){
},
leftClick(){
uni.navigateBack()
}
}
}
</script>
<style lang="scss">
.near_page{
}
</style>
Binary file not shown.

After

Width:  |  Height:  |  Size: 6.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.2 KiB

+2
View File
@@ -14,6 +14,8 @@ export default {
storeUnHandleFriendApplicationNum: (state) => state.contact.unHandleFriendApplicationNum,
storeHistoryMessageList: (state) => state.message.historyMessageList,
storeHasMoreMessage: (state) => state.message.hasMoreMessage,
storeCurrentMsg: (state) => state.message.currentMsg,
storeCurrentMsgID: (state) => state.message.currentMsgID,
storeSelfInfo: (state) => state.user.selfInfo,
storeCurrentUserID: (state) => state.user.selfInfo.userID,
storeIsSyncing: (state) => state.user.isSyncing,
+14 -10
View File
@@ -1,17 +1,14 @@
import IMSDK, {
MessageStatus,
MessageType
} from "openim-uniapp-polyfill";
import {
v4 as uuidv4
} from "uuid";
import {
UpdateMessageTypes
} from "@/constant";
import IMSDK, { MessageStatus, MessageType} from "openim-uniapp-polyfill";
import {v4 as uuidv4} from "uuid";
import {UpdateMessageTypes} from "@/constant";
const state = {
historyMessageList: [],
hasMoreMessage: true,
currentMsg: {
"clientMsgID":""
},
currentMsgID:""
};
const mutations = {
@@ -21,9 +18,16 @@ const mutations = {
SET_HAS_MORE_MESSAGE(state, hasMore) {
state.hasMoreMessage = hasMore;
},
SET_CURRENT_MSG(state, msg) {
state.currentMsg = msg;
state.currentMsgID = msg?.clientMsgID || "";
},
};
const actions = {
updateCurrentMsg({commit,state}, message) {
commit("SET_CURRENT_MSG", message);
},
async getHistoryMesageList({commit,state}, params) {
let emptyFlag = true;
try {
-3
View File
@@ -1,3 +0,0 @@
## 3.6.52022-10-14
修复组件size值为string类型时,调用toTempFilePath导出临时文件路径失败;
优化组件加载本地图片。
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1,241 +0,0 @@
const isWeex = typeof WXEnvironment !== 'undefined';
const isWeexIOS = isWeex && /ios/i.test(WXEnvironment.platform);
const isWeexAndroid = isWeex && !isWeexIOS;
import GLmethod from '../context-webgl/GLmethod';
const GCanvasModule =
(typeof weex !== 'undefined' && weex.requireModule) ? (weex.requireModule('gcanvas')) :
(typeof __weex_require__ !== 'undefined') ? (__weex_require__('@weex-module/gcanvas')) : {};
let isDebugging = false;
let isComboDisabled = false;
const logCommand = (function () {
const methodQuery = [];
Object.keys(GLmethod).forEach(key => {
methodQuery[GLmethod[key]] = key;
})
const queryMethod = (id) => {
return methodQuery[parseInt(id)] || 'NotFoundMethod';
}
const logCommand = (id, cmds) => {
const mId = cmds.split(',')[0];
const mName = queryMethod(mId);
console.log(`=== callNative - componentId:${id}; method: ${mName}; cmds: ${cmds}`);
}
return logCommand;
})();
function joinArray(arr, sep) {
let res = '';
for (let i = 0; i < arr.length; i++) {
if (i !== 0) {
res += sep;
}
res += arr[i];
}
return res;
}
const commandsCache = {}
const GBridge = {
callEnable: (ref, configArray) => {
commandsCache[ref] = [];
return GCanvasModule.enable({
componentId: ref,
config: configArray
});
},
callEnableDebug: () => {
isDebugging = true;
},
callEnableDisableCombo: () => {
isComboDisabled = true;
},
callSetContextType: function (componentId, context_type) {
GCanvasModule.setContextType(context_type, componentId);
},
callReset: function(id){
GCanvasModule.resetComponent && canvasModule.resetComponent(componentId);
},
render: isWeexIOS ? function (componentId) {
return GCanvasModule.extendCallNative({
contextId: componentId,
type: 0x60000001
});
} : function (componentId) {
return callGCanvasLinkNative(componentId, 0x60000001, 'render');
},
render2d: isWeexIOS ? function (componentId, commands, callback) {
if (isDebugging) {
console.log('>>> >>> render2d ===');
console.log('>>> commands: ' + commands);
}
GCanvasModule.render([commands, callback?true:false], componentId, callback);
} : function (componentId, commands,callback) {
if (isDebugging) {
console.log('>>> >>> render2d ===');
console.log('>>> commands: ' + commands);
}
callGCanvasLinkNative(componentId, 0x20000001, commands);
if(callback){
callback();
}
},
callExtendCallNative: isWeexIOS ? function (componentId, cmdArgs) {
throw 'should not be here anymore ' + cmdArgs;
} : function (componentId, cmdArgs) {
throw 'should not be here anymore ' + cmdArgs;
},
flushNative: isWeexIOS ? function (componentId) {
const cmdArgs = joinArray(commandsCache[componentId], ';');
commandsCache[componentId] = [];
if (isDebugging) {
console.log('>>> >>> flush native ===');
console.log('>>> commands: ' + cmdArgs);
}
const result = GCanvasModule.extendCallNative({
"contextId": componentId,
"type": 0x60000000,
"args": cmdArgs
});
const res = result && result.result;
if (isDebugging) {
console.log('>>> result: ' + res);
}
return res;
} : function (componentId) {
const cmdArgs = joinArray(commandsCache[componentId], ';');
commandsCache[componentId] = [];
if (isDebugging) {
console.log('>>> >>> flush native ===');
console.log('>>> commands: ' + cmdArgs);
}
const result = callGCanvasLinkNative(componentId, 0x60000000, cmdArgs);
if (isDebugging) {
console.log('>>> result: ' + result);
}
return result;
},
callNative: function (componentId, cmdArgs, cache) {
if (isDebugging) {
logCommand(componentId, cmdArgs);
}
commandsCache[componentId].push(cmdArgs);
if (!cache || isComboDisabled) {
return GBridge.flushNative(componentId);
} else {
return undefined;
}
},
texImage2D(componentId, ...args) {
if (isWeexIOS) {
if (args.length === 6) {
const [target, level, internalformat, format, type, image] = args;
GBridge.callNative(
componentId,
GLmethod.texImage2D + ',' + 6 + ',' + target + ',' + level + ',' + internalformat + ',' + format + ',' + type + ',' + image.src
)
} else if (args.length === 9) {
const [target, level, internalformat, width, height, border, format, type, image] = args;
GBridge.callNative(
componentId,
GLmethod.texImage2D + ',' + 9 + ',' + target + ',' + level + ',' + internalformat + ',' + width + ',' + height + ',' + border + ',' +
+ format + ',' + type + ',' + (image ? image.src : 0)
)
}
} else if (isWeexAndroid) {
if (args.length === 6) {
const [target, level, internalformat, format, type, image] = args;
GCanvasModule.texImage2D(componentId, target, level, internalformat, format, type, image.src);
} else if (args.length === 9) {
const [target, level, internalformat, width, height, border, format, type, image] = args;
GCanvasModule.texImage2D(componentId, target, level, internalformat, width, height, border, format, type, (image ? image.src : 0));
}
}
},
texSubImage2D(componentId, target, level, xoffset, yoffset, format, type, image) {
if (isWeexIOS) {
if (arguments.length === 8) {
GBridge.callNative(
componentId,
GLmethod.texSubImage2D + ',' + 6 + ',' + target + ',' + level + ',' + xoffset + ',' + yoffset, + ',' + format + ',' + type + ',' + image.src
)
}
} else if (isWeexAndroid) {
GCanvasModule.texSubImage2D(componentId, target, level, xoffset, yoffset, format, type, image.src);
}
},
bindImageTexture(componentId, src, imageId) {
GCanvasModule.bindImageTexture([src, imageId], componentId);
},
perloadImage([url, id], callback) {
GCanvasModule.preLoadImage([url, id], function (image) {
image.url = url;
image.id = id;
callback(image);
});
},
measureText(text, fontStyle, componentId) {
return GCanvasModule.measureText([text, fontStyle], componentId);
},
getImageData (componentId, x, y, w, h, callback) {
GCanvasModule.getImageData([x, y,w,h],componentId,callback);
},
putImageData (componentId, data, x, y, w, h, callback) {
GCanvasModule.putImageData([x, y,w,h,data],componentId,callback);
},
toTempFilePath(componentId, x, y, width, height, destWidth, destHeight, fileType, quality, callback){
GCanvasModule.toTempFilePath([x, y, width,height, destWidth, destHeight, fileType, quality], componentId, callback);
}
}
export default GBridge;
@@ -1,18 +0,0 @@
class FillStyleLinearGradient {
constructor(x0, y0, x1, y1) {
this._start_pos = { _x: x0, _y: y0 };
this._end_pos = { _x: x1, _y: y1 };
this._stop_count = 0;
this._stops = [0, 0, 0, 0, 0];
}
addColorStop = function (pos, color) {
if (this._stop_count < 5 && 0.0 <= pos && pos <= 1.0) {
this._stops[this._stop_count] = { _pos: pos, _color: color };
this._stop_count++;
}
}
}
export default FillStyleLinearGradient;
@@ -1,8 +0,0 @@
class FillStylePattern {
constructor(img, pattern) {
this._style = pattern;
this._img = img;
}
}
export default FillStylePattern;
@@ -1,17 +0,0 @@
class FillStyleRadialGradient {
constructor(x0, y0, r0, x1, y1, r1) {
this._start_pos = { _x: x0, _y: y0, _r: r0 };
this._end_pos = { _x: x1, _y: y1, _r: r1 };
this._stop_count = 0;
this._stops = [0, 0, 0, 0, 0];
}
addColorStop(pos, color) {
if (this._stop_count < 5 && 0.0 <= pos && pos <= 1.0) {
this._stops[this._stop_count] = { _pos: pos, _color: color };
this._stop_count++;
}
}
}
export default FillStyleRadialGradient;
@@ -1,666 +0,0 @@
import FillStylePattern from './FillStylePattern';
import FillStyleLinearGradient from './FillStyleLinearGradient';
import FillStyleRadialGradient from './FillStyleRadialGradient';
import GImage from '../env/image.js';
import {
ArrayBufferToBase64,
Base64ToUint8ClampedArray
} from '../env/tool.js';
export default class CanvasRenderingContext2D {
_drawCommands = '';
_globalAlpha = 1.0;
_fillStyle = 'rgb(0,0,0)';
_strokeStyle = 'rgb(0,0,0)';
_lineWidth = 1;
_lineCap = 'butt';
_lineJoin = 'miter';
_miterLimit = 10;
_globalCompositeOperation = 'source-over';
_textAlign = 'start';
_textBaseline = 'alphabetic';
_font = '10px sans-serif';
_savedGlobalAlpha = [];
timer = null;
componentId = null;
_notCommitDrawImageCache = [];
_needRedrawImageCache = [];
_redrawCommands = '';
_autoSaveContext = true;
// _imageMap = new GHashMap();
// _textureMap = new GHashMap();
constructor() {
this.className = 'CanvasRenderingContext2D';
//this.save()
}
setFillStyle(value) {
this.fillStyle = value;
}
set fillStyle(value) {
this._fillStyle = value;
if (typeof(value) == 'string') {
this._drawCommands = this._drawCommands.concat("F" + value + ";");
} else if (value instanceof FillStylePattern) {
const image = value._img;
if (!image.complete) {
image.onload = () => {
var index = this._needRedrawImageCache.indexOf(image);
if (index > -1) {
this._needRedrawImageCache.splice(index, 1);
CanvasRenderingContext2D.GBridge.bindImageTexture(this.componentId, image.src, image._id);
this._redrawflush(true);
}
}
this._notCommitDrawImageCache.push(image);
} else {
CanvasRenderingContext2D.GBridge.bindImageTexture(this.componentId, image.src, image._id);
}
//CanvasRenderingContext2D.GBridge.bindImageTexture(this.componentId, image.src, image._id);
this._drawCommands = this._drawCommands.concat("G" + image._id + "," + value._style + ";");
} else if (value instanceof FillStyleLinearGradient) {
var command = "D" + value._start_pos._x.toFixed(2) + "," + value._start_pos._y.toFixed(2) + "," +
value._end_pos._x.toFixed(2) + "," + value._end_pos._y.toFixed(2) + "," +
value._stop_count;
for (var i = 0; i < value._stop_count; ++i) {
command += ("," + value._stops[i]._pos + "," + value._stops[i]._color);
}
this._drawCommands = this._drawCommands.concat(command + ";");
} else if (value instanceof FillStyleRadialGradient) {
var command = "H" + value._start_pos._x.toFixed(2) + "," + value._start_pos._y.toFixed(2) + "," + value._start_pos._r
.toFixed(2) + "," +
value._end_pos._x.toFixed(2) + "," + value._end_pos._y.toFixed(2) + "," + value._end_pos._r.toFixed(2) + "," +
value._stop_count;
for (var i = 0; i < value._stop_count; ++i) {
command += ("," + value._stops[i]._pos + "," + value._stops[i]._color);
}
this._drawCommands = this._drawCommands.concat(command + ";");
}
}
get fillStyle() {
return this._fillStyle;
}
get globalAlpha() {
return this._globalAlpha;
}
setGlobalAlpha(value) {
this.globalAlpha = value;
}
set globalAlpha(value) {
this._globalAlpha = value;
this._drawCommands = this._drawCommands.concat("a" + value.toFixed(2) + ";");
}
get strokeStyle() {
return this._strokeStyle;
}
setStrokeStyle(value) {
this.strokeStyle = value;
}
set strokeStyle(value) {
this._strokeStyle = value;
if (typeof(value) == 'string') {
this._drawCommands = this._drawCommands.concat("S" + value + ";");
} else if (value instanceof FillStylePattern) {
CanvasRenderingContext2D.GBridge.bindImageTexture(this.componentId, image.src, image._id);
this._drawCommands = this._drawCommands.concat("G" + image._id + "," + value._style + ";");
} else if (value instanceof FillStyleLinearGradient) {
var command = "D" + value._start_pos._x.toFixed(2) + "," + value._start_pos._y.toFixed(2) + "," +
value._end_pos._x.toFixed(2) + "," + value._end_pos._y.toFixed(2) + "," +
value._stop_count;
for (var i = 0; i < value._stop_count; ++i) {
command += ("," + value._stops[i]._pos + "," + value._stops[i]._color);
}
this._drawCommands = this._drawCommands.concat(command + ";");
} else if (value instanceof FillStyleRadialGradient) {
var command = "H" + value._start_pos._x.toFixed(2) + "," + value._start_pos._y.toFixed(2) + "," + value._start_pos._r
.toFixed(2) + "," +
value._end_pos._x.toFixed(2) + "," + value._end_pos._y + ",".toFixed(2) + value._end_pos._r.toFixed(2) + "," +
value._stop_count;
for (var i = 0; i < value._stop_count; ++i) {
command += ("," + value._stops[i]._pos + "," + value._stops[i]._color);
}
this._drawCommands = this._drawCommands.concat(command + ";");
}
}
get lineWidth() {
return this._lineWidth;
}
setLineWidth(value) {
this.lineWidth = value;
}
set lineWidth(value) {
this._lineWidth = value;
this._drawCommands = this._drawCommands.concat("W" + value + ";");
}
get lineCap() {
return this._lineCap;
}
setLineCap(value) {
this.lineCap = value;
}
set lineCap(value) {
this._lineCap = value;
this._drawCommands = this._drawCommands.concat("C" + value + ";");
}
get lineJoin() {
return this._lineJoin;
}
setLineJoin(value) {
this.lineJoin = value
}
set lineJoin(value) {
this._lineJoin = value;
this._drawCommands = this._drawCommands.concat("J" + value + ";");
}
get miterLimit() {
return this._miterLimit;
}
setMiterLimit(value) {
this.miterLimit = value
}
set miterLimit(value) {
this._miterLimit = value;
this._drawCommands = this._drawCommands.concat("M" + value + ";");
}
get globalCompositeOperation() {
return this._globalCompositeOperation;
}
set globalCompositeOperation(value) {
this._globalCompositeOperation = value;
let mode = 0;
switch (value) {
case "source-over":
mode = 0;
break;
case "source-atop":
mode = 5;
break;
case "source-in":
mode = 0;
break;
case "source-out":
mode = 2;
break;
case "destination-over":
mode = 4;
break;
case "destination-atop":
mode = 4;
break;
case "destination-in":
mode = 4;
break;
case "destination-out":
mode = 3;
break;
case "lighter":
mode = 1;
break;
case "copy":
mode = 2;
break;
case "xor":
mode = 6;
break;
default:
mode = 0;
}
this._drawCommands = this._drawCommands.concat("B" + mode + ";");
}
get textAlign() {
return this._textAlign;
}
setTextAlign(value) {
this.textAlign = value
}
set textAlign(value) {
this._textAlign = value;
let Align = 0;
switch (value) {
case "start":
Align = 0;
break;
case "end":
Align = 1;
break;
case "left":
Align = 2;
break;
case "center":
Align = 3;
break;
case "right":
Align = 4;
break;
default:
Align = 0;
}
this._drawCommands = this._drawCommands.concat("A" + Align + ";");
}
get textBaseline() {
return this._textBaseline;
}
setTextBaseline(value) {
this.textBaseline = value
}
set textBaseline(value) {
this._textBaseline = value;
let baseline = 0;
switch (value) {
case "alphabetic":
baseline = 0;
break;
case "middle":
baseline = 1;
break;
case "top":
baseline = 2;
break;
case "hanging":
baseline = 3;
break;
case "bottom":
baseline = 4;
break;
case "ideographic":
baseline = 5;
break;
default:
baseline = 0;
break;
}
this._drawCommands = this._drawCommands.concat("E" + baseline + ";");
}
get font() {
return this._font;
}
setFontSize(size) {
var str = this._font;
var strs = str.trim().split(/\s+/);
for (var i = 0; i < strs.length; i++) {
var values = ["normal", "italic", "oblique", "normal", "small-caps", "normal", "bold",
"bolder", "lighter", "100", "200", "300", "400", "500", "600", "700", "800", "900",
"normal", "ultra-condensed", "extra-condensed", "condensed", "semi-condensed",
"semi-expanded", "expanded", "extra-expanded", "ultra-expanded"
];
if (-1 == values.indexOf(strs[i].trim())) {
if (typeof size === 'string') {
strs[i] = size;
} else if (typeof size === 'number') {
strs[i] = String(size) + 'px';
}
break;
}
}
this.font = strs.join(" ");
}
set font(value) {
this._font = value;
this._drawCommands = this._drawCommands.concat("j" + value + ";");
}
setTransform(a, b, c, d, tx, ty) {
this._drawCommands = this._drawCommands.concat("t" +
(a === 1 ? "1" : a.toFixed(2)) + "," +
(b === 0 ? "0" : b.toFixed(2)) + "," +
(c === 0 ? "0" : c.toFixed(2)) + "," +
(d === 1 ? "1" : d.toFixed(2)) + "," + tx.toFixed(2) + "," + ty.toFixed(2) + ";");
}
transform(a, b, c, d, tx, ty) {
this._drawCommands = this._drawCommands.concat("f" +
(a === 1 ? "1" : a.toFixed(2)) + "," +
(b === 0 ? "0" : b.toFixed(2)) + "," +
(c === 0 ? "0" : c.toFixed(2)) + "," +
(d === 1 ? "1" : d.toFixed(2)) + "," + tx + "," + ty + ";");
}
resetTransform() {
this._drawCommands = this._drawCommands.concat("m;");
}
scale(a, d) {
this._drawCommands = this._drawCommands.concat("k" + a.toFixed(2) + "," +
d.toFixed(2) + ";");
}
rotate(angle) {
this._drawCommands = this._drawCommands
.concat("r" + angle.toFixed(6) + ";");
}
translate(tx, ty) {
this._drawCommands = this._drawCommands.concat("l" + tx.toFixed(2) + "," + ty.toFixed(2) + ";");
}
save() {
this._savedGlobalAlpha.push(this._globalAlpha);
this._drawCommands = this._drawCommands.concat("v;");
}
restore() {
this._drawCommands = this._drawCommands.concat("e;");
this._globalAlpha = this._savedGlobalAlpha.pop();
}
createPattern(img, pattern) {
if (typeof img === 'string') {
var imgObj = new GImage();
imgObj.src = img;
img = imgObj;
}
return new FillStylePattern(img, pattern);
}
createLinearGradient(x0, y0, x1, y1) {
return new FillStyleLinearGradient(x0, y0, x1, y1);
}
createRadialGradient = function(x0, y0, r0, x1, y1, r1) {
return new FillStyleRadialGradient(x0, y0, r0, x1, y1, r1);
};
createCircularGradient = function(x0, y0, r0) {
return new FillStyleRadialGradient(x0, y0, 0, x0, y0, r0);
};
strokeRect(x, y, w, h) {
this._drawCommands = this._drawCommands.concat("s" + x + "," + y + "," + w + "," + h + ";");
}
clearRect(x, y, w, h) {
this._drawCommands = this._drawCommands.concat("c" + x + "," + y + "," + w +
"," + h + ";");
}
clip() {
this._drawCommands = this._drawCommands.concat("p;");
}
resetClip() {
this._drawCommands = this._drawCommands.concat("q;");
}
closePath() {
this._drawCommands = this._drawCommands.concat("o;");
}
moveTo(x, y) {
this._drawCommands = this._drawCommands.concat("g" + x.toFixed(2) + "," + y.toFixed(2) + ";");
}
lineTo(x, y) {
this._drawCommands = this._drawCommands.concat("i" + x.toFixed(2) + "," + y.toFixed(2) + ";");
}
quadraticCurveTo = function(cpx, cpy, x, y) {
this._drawCommands = this._drawCommands.concat("u" + cpx + "," + cpy + "," + x + "," + y + ";");
}
bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y, ) {
this._drawCommands = this._drawCommands.concat(
"z" + cp1x.toFixed(2) + "," + cp1y.toFixed(2) + "," + cp2x.toFixed(2) + "," + cp2y.toFixed(2) + "," +
x.toFixed(2) + "," + y.toFixed(2) + ";");
}
arcTo(x1, y1, x2, y2, radius) {
this._drawCommands = this._drawCommands.concat("h" + x1 + "," + y1 + "," + x2 + "," + y2 + "," + radius + ";");
}
beginPath() {
this._drawCommands = this._drawCommands.concat("b;");
}
fillRect(x, y, w, h) {
this._drawCommands = this._drawCommands.concat("n" + x + "," + y + "," + w +
"," + h + ";");
}
rect(x, y, w, h) {
this._drawCommands = this._drawCommands.concat("w" + x + "," + y + "," + w + "," + h + ";");
}
fill() {
this._drawCommands = this._drawCommands.concat("L;");
}
stroke(path) {
this._drawCommands = this._drawCommands.concat("x;");
}
arc(x, y, radius, startAngle, endAngle, anticlockwise) {
let ianticlockwise = 0;
if (anticlockwise) {
ianticlockwise = 1;
}
this._drawCommands = this._drawCommands.concat(
"y" + x.toFixed(2) + "," + y.toFixed(2) + "," +
radius.toFixed(2) + "," + startAngle + "," + endAngle + "," + ianticlockwise +
";"
);
}
fillText(text, x, y) {
let tmptext = text.replace(/!/g, "!!");
tmptext = tmptext.replace(/,/g, "!,");
tmptext = tmptext.replace(/;/g, "!;");
this._drawCommands = this._drawCommands.concat("T" + tmptext + "," + x + "," + y + ",0.0;");
}
strokeText = function(text, x, y) {
let tmptext = text.replace(/!/g, "!!");
tmptext = tmptext.replace(/,/g, "!,");
tmptext = tmptext.replace(/;/g, "!;");
this._drawCommands = this._drawCommands.concat("U" + tmptext + "," + x + "," + y + ",0.0;");
}
measureText(text) {
return CanvasRenderingContext2D.GBridge.measureText(text, this.font, this.componentId);
}
isPointInPath = function(x, y) {
throw new Error('GCanvas not supported yet');
}
drawImage(image, sx, sy, sw, sh, dx, dy, dw, dh) {
if (typeof image === 'string') {
var imgObj = new GImage();
imgObj.src = image;
image = imgObj;
}
if (image instanceof GImage) {
if (!image.complete) {
imgObj.onload = () => {
var index = this._needRedrawImageCache.indexOf(image);
if (index > -1) {
this._needRedrawImageCache.splice(index, 1);
CanvasRenderingContext2D.GBridge.bindImageTexture(this.componentId, image.src, image._id);
this._redrawflush(true);
}
}
this._notCommitDrawImageCache.push(image);
} else {
CanvasRenderingContext2D.GBridge.bindImageTexture(this.componentId, image.src, image._id);
}
var srcArgs = [image, sx, sy, sw, sh, dx, dy, dw, dh];
var args = [];
for (var arg in srcArgs) {
if (typeof(srcArgs[arg]) != 'undefined') {
args.push(srcArgs[arg]);
}
}
this.__drawImage.apply(this, args);
//this.__drawImage(image,sx, sy, sw, sh, dx, dy, dw, dh);
}
}
__drawImage(image, sx, sy, sw, sh, dx, dy, dw, dh) {
const numArgs = arguments.length;
function drawImageCommands() {
if (numArgs === 3) {
const x = parseFloat(sx) || 0.0;
const y = parseFloat(sy) || 0.0;
return ("d" + image._id + ",0,0," +
image.width + "," + image.height + "," +
x + "," + y + "," + image.width + "," + image.height + ";");
} else if (numArgs === 5) {
const x = parseFloat(sx) || 0.0;
const y = parseFloat(sy) || 0.0;
const width = parseInt(sw) || image.width;
const height = parseInt(sh) || image.height;
return ("d" + image._id + ",0,0," +
image.width + "," + image.height + "," +
x + "," + y + "," + width + "," + height + ";");
} else if (numArgs === 9) {
sx = parseFloat(sx) || 0.0;
sy = parseFloat(sy) || 0.0;
sw = parseInt(sw) || image.width;
sh = parseInt(sh) || image.height;
dx = parseFloat(dx) || 0.0;
dy = parseFloat(dy) || 0.0;
dw = parseInt(dw) || image.width;
dh = parseInt(dh) || image.height;
return ("d" + image._id + "," +
sx + "," + sy + "," + sw + "," + sh + "," +
dx + "," + dy + "," + dw + "," + dh + ";");
}
}
this._drawCommands += drawImageCommands();
}
_flush(reserve, callback) {
const commands = this._drawCommands;
this._drawCommands = '';
CanvasRenderingContext2D.GBridge.render2d(this.componentId, commands, callback);
this._needRender = false;
}
_redrawflush(reserve, callback) {
const commands = this._redrawCommands;
CanvasRenderingContext2D.GBridge.render2d(this.componentId, commands, callback);
if (this._needRedrawImageCache.length == 0) {
this._redrawCommands = '';
}
}
draw(reserve, callback) {
if (!reserve) {
this._globalAlpha = this._savedGlobalAlpha.pop();
this._savedGlobalAlpha.push(this._globalAlpha);
this._redrawCommands = this._drawCommands;
this._needRedrawImageCache = this._notCommitDrawImageCache;
if (this._autoSaveContext) {
this._drawCommands = ("v;" + this._drawCommands);
this._autoSaveContext = false;
} else {
this._drawCommands = ("e;X;v;" + this._drawCommands);
}
} else {
this._needRedrawImageCache = this._needRedrawImageCache.concat(this._notCommitDrawImageCache);
this._redrawCommands += this._drawCommands;
if (this._autoSaveContext) {
this._drawCommands = ("v;" + this._drawCommands);
this._autoSaveContext = false;
}
}
this._notCommitDrawImageCache = [];
if (this._flush) {
this._flush(reserve, callback);
}
}
getImageData(x, y, w, h, callback) {
CanvasRenderingContext2D.GBridge.getImageData(this.componentId, x, y, w, h, function(res) {
res.data = Base64ToUint8ClampedArray(res.data);
if (typeof(callback) == 'function') {
callback(res);
}
});
}
putImageData(data, x, y, w, h, callback) {
if (data instanceof Uint8ClampedArray) {
data = ArrayBufferToBase64(data);
CanvasRenderingContext2D.GBridge.putImageData(this.componentId, data, x, y, w, h, function(res) {
if (typeof(callback) == 'function') {
callback(res);
}
});
}
}
toTempFilePath(x, y, width, height, destWidth, destHeight, fileType, quality, callback) {
CanvasRenderingContext2D.GBridge.toTempFilePath(this.componentId, x, y, width, height, destWidth, destHeight,
fileType, quality,
function(res) {
if (typeof(callback) == 'function') {
callback(res);
}
});
}
}
@@ -1,11 +0,0 @@
export default class WebGLActiveInfo {
className = 'WebGLActiveInfo';
constructor({
type, name, size
}) {
this.type = type;
this.name = name;
this.size = size;
}
}
@@ -1,21 +0,0 @@
import {getTransferedObjectUUID} from './classUtils';
const name = 'WebGLBuffer';
function uuid(id) {
return getTransferedObjectUUID(name, id);
}
export default class WebGLBuffer {
className = name;
constructor(id) {
this.id = id;
}
static uuid = uuid;
uuid() {
return uuid(this.id);
}
}
@@ -1,21 +0,0 @@
import {getTransferedObjectUUID} from './classUtils';
const name = 'WebGLFrameBuffer';
function uuid(id) {
return getTransferedObjectUUID(name, id);
}
export default class WebGLFramebuffer {
className = name;
constructor(id) {
this.id = id;
}
static uuid = uuid;
uuid() {
return uuid(this.id);
}
}
@@ -1,298 +0,0 @@
export default {
"DEPTH_BUFFER_BIT": 256,
"STENCIL_BUFFER_BIT": 1024,
"COLOR_BUFFER_BIT": 16384,
"POINTS": 0,
"LINES": 1,
"LINE_LOOP": 2,
"LINE_STRIP": 3,
"TRIANGLES": 4,
"TRIANGLE_STRIP": 5,
"TRIANGLE_FAN": 6,
"ZERO": 0,
"ONE": 1,
"SRC_COLOR": 768,
"ONE_MINUS_SRC_COLOR": 769,
"SRC_ALPHA": 770,
"ONE_MINUS_SRC_ALPHA": 771,
"DST_ALPHA": 772,
"ONE_MINUS_DST_ALPHA": 773,
"DST_COLOR": 774,
"ONE_MINUS_DST_COLOR": 775,
"SRC_ALPHA_SATURATE": 776,
"FUNC_ADD": 32774,
"BLEND_EQUATION": 32777,
"BLEND_EQUATION_RGB": 32777,
"BLEND_EQUATION_ALPHA": 34877,
"FUNC_SUBTRACT": 32778,
"FUNC_REVERSE_SUBTRACT": 32779,
"BLEND_DST_RGB": 32968,
"BLEND_SRC_RGB": 32969,
"BLEND_DST_ALPHA": 32970,
"BLEND_SRC_ALPHA": 32971,
"CONSTANT_COLOR": 32769,
"ONE_MINUS_CONSTANT_COLOR": 32770,
"CONSTANT_ALPHA": 32771,
"ONE_MINUS_CONSTANT_ALPHA": 32772,
"BLEND_COLOR": 32773,
"ARRAY_BUFFER": 34962,
"ELEMENT_ARRAY_BUFFER": 34963,
"ARRAY_BUFFER_BINDING": 34964,
"ELEMENT_ARRAY_BUFFER_BINDING": 34965,
"STREAM_DRAW": 35040,
"STATIC_DRAW": 35044,
"DYNAMIC_DRAW": 35048,
"BUFFER_SIZE": 34660,
"BUFFER_USAGE": 34661,
"CURRENT_VERTEX_ATTRIB": 34342,
"FRONT": 1028,
"BACK": 1029,
"FRONT_AND_BACK": 1032,
"TEXTURE_2D": 3553,
"CULL_FACE": 2884,
"BLEND": 3042,
"DITHER": 3024,
"STENCIL_TEST": 2960,
"DEPTH_TEST": 2929,
"SCISSOR_TEST": 3089,
"POLYGON_OFFSET_FILL": 32823,
"SAMPLE_ALPHA_TO_COVERAGE": 32926,
"SAMPLE_COVERAGE": 32928,
"NO_ERROR": 0,
"INVALID_ENUM": 1280,
"INVALID_VALUE": 1281,
"INVALID_OPERATION": 1282,
"OUT_OF_MEMORY": 1285,
"CW": 2304,
"CCW": 2305,
"LINE_WIDTH": 2849,
"ALIASED_POINT_SIZE_RANGE": 33901,
"ALIASED_LINE_WIDTH_RANGE": 33902,
"CULL_FACE_MODE": 2885,
"FRONT_FACE": 2886,
"DEPTH_RANGE": 2928,
"DEPTH_WRITEMASK": 2930,
"DEPTH_CLEAR_VALUE": 2931,
"DEPTH_FUNC": 2932,
"STENCIL_CLEAR_VALUE": 2961,
"STENCIL_FUNC": 2962,
"STENCIL_FAIL": 2964,
"STENCIL_PASS_DEPTH_FAIL": 2965,
"STENCIL_PASS_DEPTH_PASS": 2966,
"STENCIL_REF": 2967,
"STENCIL_VALUE_MASK": 2963,
"STENCIL_WRITEMASK": 2968,
"STENCIL_BACK_FUNC": 34816,
"STENCIL_BACK_FAIL": 34817,
"STENCIL_BACK_PASS_DEPTH_FAIL": 34818,
"STENCIL_BACK_PASS_DEPTH_PASS": 34819,
"STENCIL_BACK_REF": 36003,
"STENCIL_BACK_VALUE_MASK": 36004,
"STENCIL_BACK_WRITEMASK": 36005,
"VIEWPORT": 2978,
"SCISSOR_BOX": 3088,
"COLOR_CLEAR_VALUE": 3106,
"COLOR_WRITEMASK": 3107,
"UNPACK_ALIGNMENT": 3317,
"PACK_ALIGNMENT": 3333,
"MAX_TEXTURE_SIZE": 3379,
"MAX_VIEWPORT_DIMS": 3386,
"SUBPIXEL_BITS": 3408,
"RED_BITS": 3410,
"GREEN_BITS": 3411,
"BLUE_BITS": 3412,
"ALPHA_BITS": 3413,
"DEPTH_BITS": 3414,
"STENCIL_BITS": 3415,
"POLYGON_OFFSET_UNITS": 10752,
"POLYGON_OFFSET_FACTOR": 32824,
"TEXTURE_BINDING_2D": 32873,
"SAMPLE_BUFFERS": 32936,
"SAMPLES": 32937,
"SAMPLE_COVERAGE_VALUE": 32938,
"SAMPLE_COVERAGE_INVERT": 32939,
"COMPRESSED_TEXTURE_FORMATS": 34467,
"DONT_CARE": 4352,
"FASTEST": 4353,
"NICEST": 4354,
"GENERATE_MIPMAP_HINT": 33170,
"BYTE": 5120,
"UNSIGNED_BYTE": 5121,
"SHORT": 5122,
"UNSIGNED_SHORT": 5123,
"INT": 5124,
"UNSIGNED_INT": 5125,
"FLOAT": 5126,
"DEPTH_COMPONENT": 6402,
"ALPHA": 6406,
"RGB": 6407,
"RGBA": 6408,
"LUMINANCE": 6409,
"LUMINANCE_ALPHA": 6410,
"UNSIGNED_SHORT_4_4_4_4": 32819,
"UNSIGNED_SHORT_5_5_5_1": 32820,
"UNSIGNED_SHORT_5_6_5": 33635,
"FRAGMENT_SHADER": 35632,
"VERTEX_SHADER": 35633,
"MAX_VERTEX_ATTRIBS": 34921,
"MAX_VERTEX_UNIFORM_VECTORS": 36347,
"MAX_VARYING_VECTORS": 36348,
"MAX_COMBINED_TEXTURE_IMAGE_UNITS": 35661,
"MAX_VERTEX_TEXTURE_IMAGE_UNITS": 35660,
"MAX_TEXTURE_IMAGE_UNITS": 34930,
"MAX_FRAGMENT_UNIFORM_VECTORS": 36349,
"SHADER_TYPE": 35663,
"DELETE_STATUS": 35712,
"LINK_STATUS": 35714,
"VALIDATE_STATUS": 35715,
"ATTACHED_SHADERS": 35717,
"ACTIVE_UNIFORMS": 35718,
"ACTIVE_ATTRIBUTES": 35721,
"SHADING_LANGUAGE_VERSION": 35724,
"CURRENT_PROGRAM": 35725,
"NEVER": 512,
"LESS": 513,
"EQUAL": 514,
"LEQUAL": 515,
"GREATER": 516,
"NOTEQUAL": 517,
"GEQUAL": 518,
"ALWAYS": 519,
"KEEP": 7680,
"REPLACE": 7681,
"INCR": 7682,
"DECR": 7683,
"INVERT": 5386,
"INCR_WRAP": 34055,
"DECR_WRAP": 34056,
"VENDOR": 7936,
"RENDERER": 7937,
"VERSION": 7938,
"NEAREST": 9728,
"LINEAR": 9729,
"NEAREST_MIPMAP_NEAREST": 9984,
"LINEAR_MIPMAP_NEAREST": 9985,
"NEAREST_MIPMAP_LINEAR": 9986,
"LINEAR_MIPMAP_LINEAR": 9987,
"TEXTURE_MAG_FILTER": 10240,
"TEXTURE_MIN_FILTER": 10241,
"TEXTURE_WRAP_S": 10242,
"TEXTURE_WRAP_T": 10243,
"TEXTURE": 5890,
"TEXTURE_CUBE_MAP": 34067,
"TEXTURE_BINDING_CUBE_MAP": 34068,
"TEXTURE_CUBE_MAP_POSITIVE_X": 34069,
"TEXTURE_CUBE_MAP_NEGATIVE_X": 34070,
"TEXTURE_CUBE_MAP_POSITIVE_Y": 34071,
"TEXTURE_CUBE_MAP_NEGATIVE_Y": 34072,
"TEXTURE_CUBE_MAP_POSITIVE_Z": 34073,
"TEXTURE_CUBE_MAP_NEGATIVE_Z": 34074,
"MAX_CUBE_MAP_TEXTURE_SIZE": 34076,
"TEXTURE0": 33984,
"TEXTURE1": 33985,
"TEXTURE2": 33986,
"TEXTURE3": 33987,
"TEXTURE4": 33988,
"TEXTURE5": 33989,
"TEXTURE6": 33990,
"TEXTURE7": 33991,
"TEXTURE8": 33992,
"TEXTURE9": 33993,
"TEXTURE10": 33994,
"TEXTURE11": 33995,
"TEXTURE12": 33996,
"TEXTURE13": 33997,
"TEXTURE14": 33998,
"TEXTURE15": 33999,
"TEXTURE16": 34000,
"TEXTURE17": 34001,
"TEXTURE18": 34002,
"TEXTURE19": 34003,
"TEXTURE20": 34004,
"TEXTURE21": 34005,
"TEXTURE22": 34006,
"TEXTURE23": 34007,
"TEXTURE24": 34008,
"TEXTURE25": 34009,
"TEXTURE26": 34010,
"TEXTURE27": 34011,
"TEXTURE28": 34012,
"TEXTURE29": 34013,
"TEXTURE30": 34014,
"TEXTURE31": 34015,
"ACTIVE_TEXTURE": 34016,
"REPEAT": 10497,
"CLAMP_TO_EDGE": 33071,
"MIRRORED_REPEAT": 33648,
"FLOAT_VEC2": 35664,
"FLOAT_VEC3": 35665,
"FLOAT_VEC4": 35666,
"INT_VEC2": 35667,
"INT_VEC3": 35668,
"INT_VEC4": 35669,
"BOOL": 35670,
"BOOL_VEC2": 35671,
"BOOL_VEC3": 35672,
"BOOL_VEC4": 35673,
"FLOAT_MAT2": 35674,
"FLOAT_MAT3": 35675,
"FLOAT_MAT4": 35676,
"SAMPLER_2D": 35678,
"SAMPLER_CUBE": 35680,
"VERTEX_ATTRIB_ARRAY_ENABLED": 34338,
"VERTEX_ATTRIB_ARRAY_SIZE": 34339,
"VERTEX_ATTRIB_ARRAY_STRIDE": 34340,
"VERTEX_ATTRIB_ARRAY_TYPE": 34341,
"VERTEX_ATTRIB_ARRAY_NORMALIZED": 34922,
"VERTEX_ATTRIB_ARRAY_POINTER": 34373,
"VERTEX_ATTRIB_ARRAY_BUFFER_BINDING": 34975,
"IMPLEMENTATION_COLOR_READ_TYPE": 35738,
"IMPLEMENTATION_COLOR_READ_FORMAT": 35739,
"COMPILE_STATUS": 35713,
"LOW_FLOAT": 36336,
"MEDIUM_FLOAT": 36337,
"HIGH_FLOAT": 36338,
"LOW_INT": 36339,
"MEDIUM_INT": 36340,
"HIGH_INT": 36341,
"FRAMEBUFFER": 36160,
"RENDERBUFFER": 36161,
"RGBA4": 32854,
"RGB5_A1": 32855,
"RGB565": 36194,
"DEPTH_COMPONENT16": 33189,
"STENCIL_INDEX8": 36168,
"DEPTH_STENCIL": 34041,
"RENDERBUFFER_WIDTH": 36162,
"RENDERBUFFER_HEIGHT": 36163,
"RENDERBUFFER_INTERNAL_FORMAT": 36164,
"RENDERBUFFER_RED_SIZE": 36176,
"RENDERBUFFER_GREEN_SIZE": 36177,
"RENDERBUFFER_BLUE_SIZE": 36178,
"RENDERBUFFER_ALPHA_SIZE": 36179,
"RENDERBUFFER_DEPTH_SIZE": 36180,
"RENDERBUFFER_STENCIL_SIZE": 36181,
"FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE": 36048,
"FRAMEBUFFER_ATTACHMENT_OBJECT_NAME": 36049,
"FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL": 36050,
"FRAMEBUFFER_ATTACHMENT_TEXTURE_CUBE_MAP_FACE": 36051,
"COLOR_ATTACHMENT0": 36064,
"DEPTH_ATTACHMENT": 36096,
"STENCIL_ATTACHMENT": 36128,
"DEPTH_STENCIL_ATTACHMENT": 33306,
"NONE": 0,
"FRAMEBUFFER_COMPLETE": 36053,
"FRAMEBUFFER_INCOMPLETE_ATTACHMENT": 36054,
"FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT": 36055,
"FRAMEBUFFER_INCOMPLETE_DIMENSIONS": 36057,
"FRAMEBUFFER_UNSUPPORTED": 36061,
"FRAMEBUFFER_BINDING": 36006,
"RENDERBUFFER_BINDING": 36007,
"MAX_RENDERBUFFER_SIZE": 34024,
"INVALID_FRAMEBUFFER_OPERATION": 1286,
"UNPACK_FLIP_Y_WEBGL": 37440,
"UNPACK_PREMULTIPLY_ALPHA_WEBGL": 37441,
"CONTEXT_LOST_WEBGL": 37442,
"UNPACK_COLORSPACE_CONVERSION_WEBGL": 37443,
"BROWSER_DEFAULT_WEBGL": 37444
};
@@ -1,142 +0,0 @@
let i = 1;
const GLmethod = {};
GLmethod.activeTexture = i++; //1
GLmethod.attachShader = i++;
GLmethod.bindAttribLocation = i++;
GLmethod.bindBuffer = i++;
GLmethod.bindFramebuffer = i++;
GLmethod.bindRenderbuffer = i++;
GLmethod.bindTexture = i++;
GLmethod.blendColor = i++;
GLmethod.blendEquation = i++;
GLmethod.blendEquationSeparate = i++; //10
GLmethod.blendFunc = i++;
GLmethod.blendFuncSeparate = i++;
GLmethod.bufferData = i++;
GLmethod.bufferSubData = i++;
GLmethod.checkFramebufferStatus = i++;
GLmethod.clear = i++;
GLmethod.clearColor = i++;
GLmethod.clearDepth = i++;
GLmethod.clearStencil = i++;
GLmethod.colorMask = i++; //20
GLmethod.compileShader = i++;
GLmethod.compressedTexImage2D = i++;
GLmethod.compressedTexSubImage2D = i++;
GLmethod.copyTexImage2D = i++;
GLmethod.copyTexSubImage2D = i++;
GLmethod.createBuffer = i++;
GLmethod.createFramebuffer = i++;
GLmethod.createProgram = i++;
GLmethod.createRenderbuffer = i++;
GLmethod.createShader = i++; //30
GLmethod.createTexture = i++;
GLmethod.cullFace = i++;
GLmethod.deleteBuffer = i++;
GLmethod.deleteFramebuffer = i++;
GLmethod.deleteProgram = i++;
GLmethod.deleteRenderbuffer = i++;
GLmethod.deleteShader = i++;
GLmethod.deleteTexture = i++;
GLmethod.depthFunc = i++;
GLmethod.depthMask = i++; //40
GLmethod.depthRange = i++;
GLmethod.detachShader = i++;
GLmethod.disable = i++;
GLmethod.disableVertexAttribArray = i++;
GLmethod.drawArrays = i++;
GLmethod.drawArraysInstancedANGLE = i++;
GLmethod.drawElements = i++;
GLmethod.drawElementsInstancedANGLE = i++;
GLmethod.enable = i++;
GLmethod.enableVertexAttribArray = i++; //50
GLmethod.flush = i++;
GLmethod.framebufferRenderbuffer = i++;
GLmethod.framebufferTexture2D = i++;
GLmethod.frontFace = i++;
GLmethod.generateMipmap = i++;
GLmethod.getActiveAttrib = i++;
GLmethod.getActiveUniform = i++;
GLmethod.getAttachedShaders = i++;
GLmethod.getAttribLocation = i++;
GLmethod.getBufferParameter = i++; //60
GLmethod.getContextAttributes = i++;
GLmethod.getError = i++;
GLmethod.getExtension = i++;
GLmethod.getFramebufferAttachmentParameter = i++;
GLmethod.getParameter = i++;
GLmethod.getProgramInfoLog = i++;
GLmethod.getProgramParameter = i++;
GLmethod.getRenderbufferParameter = i++;
GLmethod.getShaderInfoLog = i++;
GLmethod.getShaderParameter = i++; //70
GLmethod.getShaderPrecisionFormat = i++;
GLmethod.getShaderSource = i++;
GLmethod.getSupportedExtensions = i++;
GLmethod.getTexParameter = i++;
GLmethod.getUniform = i++;
GLmethod.getUniformLocation = i++;
GLmethod.getVertexAttrib = i++;
GLmethod.getVertexAttribOffset = i++;
GLmethod.isBuffer = i++;
GLmethod.isContextLost = i++; //80
GLmethod.isEnabled = i++;
GLmethod.isFramebuffer = i++;
GLmethod.isProgram = i++;
GLmethod.isRenderbuffer = i++;
GLmethod.isShader = i++;
GLmethod.isTexture = i++;
GLmethod.lineWidth = i++;
GLmethod.linkProgram = i++;
GLmethod.pixelStorei = i++;
GLmethod.polygonOffset = i++; //90
GLmethod.readPixels = i++;
GLmethod.renderbufferStorage = i++;
GLmethod.sampleCoverage = i++;
GLmethod.scissor = i++;
GLmethod.shaderSource = i++;
GLmethod.stencilFunc = i++;
GLmethod.stencilFuncSeparate = i++;
GLmethod.stencilMask = i++;
GLmethod.stencilMaskSeparate = i++;
GLmethod.stencilOp = i++; //100
GLmethod.stencilOpSeparate = i++;
GLmethod.texImage2D = i++;
GLmethod.texParameterf = i++;
GLmethod.texParameteri = i++;
GLmethod.texSubImage2D = i++;
GLmethod.uniform1f = i++;
GLmethod.uniform1fv = i++;
GLmethod.uniform1i = i++;
GLmethod.uniform1iv = i++;
GLmethod.uniform2f = i++; //110
GLmethod.uniform2fv = i++;
GLmethod.uniform2i = i++;
GLmethod.uniform2iv = i++;
GLmethod.uniform3f = i++;
GLmethod.uniform3fv = i++;
GLmethod.uniform3i = i++;
GLmethod.uniform3iv = i++;
GLmethod.uniform4f = i++;
GLmethod.uniform4fv = i++;
GLmethod.uniform4i = i++; //120
GLmethod.uniform4iv = i++;
GLmethod.uniformMatrix2fv = i++;
GLmethod.uniformMatrix3fv = i++;
GLmethod.uniformMatrix4fv = i++;
GLmethod.useProgram = i++;
GLmethod.validateProgram = i++;
GLmethod.vertexAttrib1f = i++; //new
GLmethod.vertexAttrib2f = i++; //new
GLmethod.vertexAttrib3f = i++; //new
GLmethod.vertexAttrib4f = i++; //new //130
GLmethod.vertexAttrib1fv = i++; //new
GLmethod.vertexAttrib2fv = i++; //new
GLmethod.vertexAttrib3fv = i++; //new
GLmethod.vertexAttrib4fv = i++; //new
GLmethod.vertexAttribPointer = i++;
GLmethod.viewport = i++;
export default GLmethod;
@@ -1,23 +0,0 @@
const GLtype = {};
[
"GLbitfield",
"GLboolean",
"GLbyte",
"GLclampf",
"GLenum",
"GLfloat",
"GLint",
"GLintptr",
"GLsizei",
"GLsizeiptr",
"GLshort",
"GLubyte",
"GLuint",
"GLushort"
].sort().map((typeName, i) => GLtype[typeName] = 1 >> (i + 1));
export default GLtype;
@@ -1,21 +0,0 @@
import {getTransferedObjectUUID} from './classUtils';
const name = 'WebGLProgram';
function uuid(id) {
return getTransferedObjectUUID(name, id);
}
export default class WebGLProgram {
className = name;
constructor(id) {
this.id = id;
}
static uuid = uuid;
uuid() {
return uuid(this.id);
}
}
@@ -1,21 +0,0 @@
import {getTransferedObjectUUID} from './classUtils';
const name = 'WebGLRenderBuffer';
function uuid(id) {
return getTransferedObjectUUID(name, id);
}
export default class WebGLRenderbuffer {
className = name;
constructor(id) {
this.id = id;
}
static uuid = uuid;
uuid() {
return uuid(this.id);
}
}
File diff suppressed because it is too large Load Diff
@@ -1,22 +0,0 @@
import {getTransferedObjectUUID} from './classUtils';
const name = 'WebGLShader';
function uuid(id) {
return getTransferedObjectUUID(name, id);
}
export default class WebGLShader {
className = name;
constructor(id, type) {
this.id = id;
this.type = type;
}
static uuid = uuid;
uuid() {
return uuid(this.id);
}
}
@@ -1,11 +0,0 @@
export default class WebGLShaderPrecisionFormat {
className = 'WebGLShaderPrecisionFormat';
constructor({
rangeMin, rangeMax, precision
}) {
this.rangeMin = rangeMin;
this.rangeMax = rangeMax;
this.precision = precision;
}
}
@@ -1,22 +0,0 @@
import {getTransferedObjectUUID} from './classUtils';
const name = 'WebGLTexture';
function uuid(id) {
return getTransferedObjectUUID(name, id);
}
export default class WebGLTexture {
className = name;
constructor(id, type) {
this.id = id;
this.type = type;
}
static uuid = uuid;
uuid() {
return uuid(this.id);
}
}
@@ -1,22 +0,0 @@
import {getTransferedObjectUUID} from './classUtils';
const name = 'WebGLUniformLocation';
function uuid(id) {
return getTransferedObjectUUID(name, id);
}
export default class WebGLUniformLocation {
className = name;
constructor(id, type) {
this.id = id;
this.type = type;
}
static uuid = uuid;
uuid() {
return uuid(this.id);
}
}
@@ -1,3 +0,0 @@
export function getTransferedObjectUUID(name, id) {
return `${name.toLowerCase()}-${id}`;
}
-74
View File
@@ -1,74 +0,0 @@
import GContext2D from '../context-2d/RenderingContext';
import GContextWebGL from '../context-webgl/RenderingContext';
export default class GCanvas {
// static GBridge = null;
id = null;
_needRender = true;
constructor(id, { disableAutoSwap }) {
this.id = id;
this._disableAutoSwap = disableAutoSwap;
if (disableAutoSwap) {
this._swapBuffers = () => {
GCanvas.GBridge.render(this.id);
}
}
}
getContext(type) {
let context = null;
if (type.match(/webgl/i)) {
context = new GContextWebGL(this);
context.componentId = this.id;
if (!this._disableAutoSwap) {
const render = () => {
if (this._needRender) {
GCanvas.GBridge.render(this.id);
this._needRender = false;
}
}
setInterval(render, 16);
}
GCanvas.GBridge.callSetContextType(this.id, 1); // 0 for 2d; 1 for webgl
} else if (type.match(/2d/i)) {
context = new GContext2D(this);
context.componentId = this.id;
// const render = ( callback ) => {
//
// const commands = context._drawCommands;
// context._drawCommands = '';
//
// GCanvas.GBridge.render2d(this.id, commands, callback);
// this._needRender = false;
// }
// //draw方法触发
// context._flush = render;
// //setInterval(render, 16);
GCanvas.GBridge.callSetContextType(this.id, 0);
} else {
throw new Error('not supported context ' + type);
}
return context;
}
reset() {
GCanvas.GBridge.callReset(this.id);
}
}
-96
View File
@@ -1,96 +0,0 @@
let incId = 1;
const noop = function () { };
class GImage {
static GBridge = null;
constructor() {
this._id = incId++;
this._width = 0;
this._height = 0;
this._src = undefined;
this._onload = noop;
this._onerror = noop;
this.complete = false;
}
get width() {
return this._width;
}
set width(v) {
this._width = v;
}
get height() {
return this._height;
}
set height(v) {
this._height = v;
}
get src() {
return this._src;
}
set src(v) {
if (v.startsWith('//')) {
v = 'http:' + v;
}
this._src = v;
GImage.GBridge.perloadImage([this._src, this._id], (data) => {
if (typeof data === 'string') {
data = JSON.parse(data);
}
if (data.error) {
var evt = { type: 'error', target: this };
this.onerror(evt);
} else {
this.complete = true;
this.width = typeof data.width === 'number' ? data.width : 0;
this.height = typeof data.height === 'number' ? data.height : 0;
var evt = { type: 'load', target: this };
this.onload(evt);
}
});
}
addEventListener(name, listener) {
if (name === 'load') {
this.onload = listener;
} else if (name === 'error') {
this.onerror = listener;
}
}
removeEventListener(name, listener) {
if (name === 'load') {
this.onload = noop;
} else if (name === 'error') {
this.onerror = noop;
}
}
get onload() {
return this._onload;
}
set onload(v) {
this._onload = v;
}
get onerror() {
return this._onerror;
}
set onerror(v) {
this._onerror = v;
}
}
export default GImage;
-24
View File
@@ -1,24 +0,0 @@
export function ArrayBufferToBase64 (buffer) {
var binary = '';
var bytes = new Uint8ClampedArray(buffer);
for (var len = bytes.byteLength, i = 0; i < len; i++) {
binary += String.fromCharCode(bytes[i]);
}
return btoa(binary);
}
export function Base64ToUint8ClampedArray(base64String) {
const padding = '='.repeat((4 - base64String.length % 4) % 4);
const base64 = (base64String + padding)
.replace(/\-/g, '+')
.replace(/_/g, '/');
const rawData = atob(base64);
const outputArray = new Uint8ClampedArray(rawData.length);
for (let i = 0; i < rawData.length; ++i) {
outputArray[i] = rawData.charCodeAt(i);
}
return outputArray;
}
@@ -1,39 +0,0 @@
import GCanvas from './env/canvas';
import GImage from './env/image';
import GWebGLRenderingContext from './context-webgl/RenderingContext';
import GContext2D from './context-2d/RenderingContext';
import GBridgeWeex from './bridge/bridge-weex';
export let Image = GImage;
export let WeexBridge = GBridgeWeex;
export function enable(el, { bridge, debug, disableAutoSwap, disableComboCommands } = {}) {
const GBridge = GImage.GBridge = GCanvas.GBridge = GWebGLRenderingContext.GBridge = GContext2D.GBridge = bridge;
GBridge.callEnable(el.ref, [
0, // renderMode: 0--RENDERMODE_WHEN_DIRTY, 1--RENDERMODE_CONTINUOUSLY
-1, // hybridLayerType: 0--LAYER_TYPE_NONE 1--LAYER_TYPE_SOFTWARE 2--LAYER_TYPE_HARDWARE
false, // supportScroll
false, // newCanvasMode
1, // compatible
'white',// clearColor
false // sameLevel: newCanvasMode = true && true => GCanvasView and Webview is same level
]);
if (debug === true) {
GBridge.callEnableDebug();
}
if (disableComboCommands) {
GBridge.callEnableDisableCombo();
}
var canvas = new GCanvas(el.ref, { disableAutoSwap });
canvas.width = el.style.width;
canvas.height = el.style.height;
return canvas;
};
@@ -1,12 +0,0 @@
{
"name": "uqrcode",
"version": "3.6.0",
"description": "",
"main": "uqrcode.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC"
}
File diff suppressed because one or more lines are too long
-201
View File
@@ -1,201 +0,0 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-83
View File
@@ -1,83 +0,0 @@
{
"id": "Sansnn-uQRCode",
"displayName": "uQRCode 全端二维码生成插件 支持nvue 支持nodejs服务端",
"version": "3.6.5",
"description": "uQRCode是一款基于Javascript环境开发的二维码生成插件,适用所有Javascript运行环境的前端应用和Node.js。",
"keywords": [
"二维码",
"uQRCode",
"qrcode",
"qr"
],
"repository": "https://github.com/Sansnn/uQRCode",
"engines": {
"HBuilderX": "^3.1.0"
},
"dcloudext": {
"category": [
"JS SDK",
"通用 SDK"
],
"sale": {
"regular": {
"price": "0.00"
},
"sourcecode": {
"price": "0.00"
}
},
"contact": {
"qq": ""
},
"declaration": {
"ads": "无",
"data": "无",
"permissions": "无"
},
"npmurl": "https://www.npmjs.com/package/uqrcodejs"
},
"uni_modules": {
"dependencies": [],
"encrypt": [],
"platforms": {
"cloud": {
"tcb": "y",
"aliyun": "y"
},
"client": {
"App": {
"app-vue": "y",
"app-nvue": "y"
},
"H5-mobile": {
"Safari": "y",
"Android Browser": "y",
"微信浏览器(Android)": "y",
"QQ浏览器(Android)": "y"
},
"H5-pc": {
"Chrome": "y",
"IE": "y",
"Edge": "y",
"Firefox": "y",
"Safari": "y"
},
"小程序": {
"微信": "y",
"阿里": "y",
"百度": "y",
"字节跳动": "y",
"QQ": "y"
},
"快应用": {
"华为": "y",
"联盟": "y"
},
"Vue": {
"vue2": "y",
"vue3": "y"
}
}
}
}
}
-318
View File
@@ -1,318 +0,0 @@
# 介绍
`uQRCode`是一款基于`Javascript`环境开发的二维码生成插件,适用所有`Javascript`运行环境的前端应用和`Node.js`应用。
`uQRCode`可扩展性高,它支持自定义渲染二维码,可通过`uQRCode API`得到二维码绘制关键信息后,使用`canvas``svg``js`操作`dom`的方式绘制二维码图案。还可自定义二维码样式,如随机颜色、圆点、方块、块与块之间的间距等。
欢迎加入群聊【uQRCode交流群】:[695070434](https://jq.qq.com/?_wv=1027&k=JRjzDqiw)。
# 设计器
uQRCode发布了配套的可视化设计器,可根据自己喜好在设计器中设计二维码样式,一键生成配置代码复制到项目中,详情请在微信小程序搜索“柚子二维码”,或扫描下方小程序码体验。
![uQRCode设计器](https://doc.uqrcode.cn/mp_weixin_code.jpg)
## 设计器模板示例
![uQRCode设计器](https://doc.uqrcode.cn/yz_1.png)
![uQRCode设计器](https://doc.uqrcode.cn/yz_2.png)
![uQRCode设计器](https://doc.uqrcode.cn/yz_3.png)
![uQRCode设计器](https://doc.uqrcode.cn/yz_4.png)
![uQRCode设计器](https://doc.uqrcode.cn/yz_5.png)
![uQRCode设计器](https://doc.uqrcode.cn/yz_6.png)
![uQRCode设计器](https://doc.uqrcode.cn/yz_7.png)
![uQRCode设计器](https://doc.uqrcode.cn/yz_8.png)
![uQRCode设计器](https://doc.uqrcode.cn/yz_9.png)
# 快速上手
> 在`uni-app`中,我们更推荐使用组件方式来生成二维码,组件方式大大提高了页面的可读性以及避开了一些平台容易出问题的地方,当组件无法满足需求的时候,再考虑切换成原生方式。
官方文档:[https://doc.uqrcode.cn](https://doc.uqrcode.cn)。
github地址:[https://github.com/Sansnn/uQRCode](https://github.com/Sansnn/uQRCode)。
npm地址:[https://www.npmjs.com/package/uqrcodejs](https://www.npmjs.com/package/uqrcodejs)。
uni-app插件市场地址:[https://ext.dcloud.net.cn/plugin?id=1287](https://ext.dcloud.net.cn/plugin?id=1287)。
## 原生方式
原生方式仅需要获取`uqrcode.js`文件便可使用。详细配置请移步到:文档 > [原生](https://doc.uqrcode.cn/document/native.html)。
### 安装
1. 通过`npm`安装,成功后即可使用`import``require`进行引用。
``` bash
# npm安装
npm install uqrcodejs
# 或者
npm install @uqrcode/js
```
2. 通过项目开源地址获取`uqrcode.js`,下载`uqrcode.js`后,将其复制到您项目指定目录,在页面中引入`uqrcode.js`文件即可开始使用。
### 引入
- 通过`import`引入。
``` javascript
// npm安装
import UQRCode from 'uqrcodejs'; // npm install uqrcodejs
// 或者
import UQRCode from '@uqrcode/js'; // npm install @uqrcode/js
```
- `Node.js`通过`require`引入。
``` javascript
// npm安装
const UQRCode = require('uqrcodejs'); // npm install uqrcodejs
// 或者
const UQRCode = require('@uqrcode/js'); // npm install @uqrcode/js
```
- 原生浏览器环境,在js脚本加载时添加到`window`。
``` html
<script type="text/javascript" src="uqrcode.js"></script>
<script>
var UQRCode = window.UQRCode;
</script>
```
### 简单用法
`uQRCode`基于`Canvas API`封装了一套方法,建议开发者使用`canvas`生成,一键调用,非常方便。以下是示例:
- HTML部分
``` html
<canvas id="qrcode" width="200" height="200"></canvas>
```
- JS部分
``` javascript
// 获取uQRCode实例
var qr = new UQRCode();
// 设置二维码内容
qr.data = "https://doc.uqrcode.cn";
// 设置二维码大小,必须与canvas设置的宽高一致
qr.size = 200;
// 调用制作二维码方法
qr.make();
// 获取canvas元素
var canvas = document.getElementById("qrcode");
// 获取canvas上下文
var canvasContext = canvas.getContext("2d");
// 设置uQRCode实例的canvas上下文
qr.canvasContext = canvasContext;
// 调用绘制方法将二维码图案绘制到canvas上
qr.drawCanvas();
```
### 高级用法
考虑到部分平台可能不支持`canvas`,所以`uQRCode`并没有强制要求和`canvas`一起使用,您还可以选择其他方式来生成二维码,例如使用`js`操作`dom`进行绘制或是使用`svg`绘制等。以下是示例:
- js操作dom
``` html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>uQRCode二维码生成</title>
</head>
<body>
<div id="qrcode" style="position: relative;"></div>
<script type="text/javascript" src="uqrcode.js"></script>
<script>
// 引入uQRCode
var UQRCode = window.UQRCode;
// 获取uQRCode实例
var qr = new UQRCode();
// 设置二维码内容
qr.data = "https://doc.uqrcode.cn";
// 设置二维码大小,必须与canvas设置的宽高一致
qr.size = 200;
// 设置二维码前景图,可以是路径
qr.foregroundImageSrc =
'data:image/png;base64,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';
// 调用制作二维码方法
qr.make();
// 遍历drawModules创建dom元素
var qrHtml = '';
for (var i = 0; i < qr.drawModules.length; i++) {
var drawModule = qr.drawModules[i];
switch (drawModule.type) {
case 'block':
/* 绘制小块 */
qrHtml += `<div style="position: absolute;left: ${drawModule.x}px;top: ${drawModule.y}px;width: ${drawModule.width}px;height: ${drawModule.height}px;background: ${drawModule.color};"></div>`;
break;
case 'image':
/* 绘制图像 */
qrHtml += `<img style="position: absolute;left: ${drawModule.x}px;top: ${drawModule.y}px;width: ${drawModule.width}px;height: ${drawModule.height}px;" src="${drawModule.imageSrc}" />`;
break;
}
}
document.getElementById('qrcode').innerHTML = qrHtml;
</script>
</body>
</html>
```
- svg
``` html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>uQRCode二维码生成</title>
</head>
<body>
<svg id="qrcode" width="200" height="200" xmlns="http://www.w3.org/2000/svg" version="1.1"></svg>
<script type="text/javascript" src="uqrcode.js"></script>
<script>
// 引入uQRCode
var UQRCode = window.UQRCode;
// 获取uQRCode实例
var qr = new UQRCode();
// 设置二维码内容
qr.data = "https://doc.uqrcode.cn";
// 设置二维码大小,必须与canvas设置的宽高一致
qr.size = 200;
// 设置二维码前景图,可以是路径
qr.foregroundImageSrc =
'data:image/png;base64,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';
// 调用制作二维码方法
qr.make();
// 遍历drawModules创建svg元素
var qrHtml = '';
for (var i = 0; i < qr.drawModules.length; i++) {
var drawModule = qr.drawModules[i];
switch (drawModule.type) {
case 'block':
/* 绘制小块 */
qrHtml += `<rect x="${drawModule.x}" y="${drawModule.y}" width="${drawModule.width}" height="${drawModule.height}" style="fill: ${drawModule.color};" />`;
break;
case 'image':
/* 绘制图像 */
qrHtml += `<image href="${drawModule.imageSrc}" x="${drawModule.x}" y="${drawModule.y}" width="${drawModule.width}" height="${drawModule.height}" />`;
break;
}
}
document.getElementById('qrcode').innerHTML = qrHtml;
</script>
</body>
</html>
```
> 更多用法大家自行探索咯,期待分享哟~
### 导出临时文件路径
原生方式基于`Canvas`的,请自行参阅各平台`Canvas`的导出方式。以下是部分示例:
- uni-app
```javascript
// 通过uni.createCanvasContext方式创建绘制上下文的,对应导出API为uni.canvasToTempFilePath
// 调用完ctx.draw()方法后不能第一时间导出,否则会异常,需要有一定的延时
setTimeout(() => {
uni.canvasToTempFilePath(
{
canvasId: this.canvasId,
fileType: this.fileType,
width: this.canvasWidth,
height: this.canvasHeight,
success: res => {
console.log(res);
},
fail: err => {
console.log(err);
}
},
// this // 组件内使用必传当前实例
);
}, 300);
```
- Canvas2D
```javascript
// 得到base64
console.log(canvas.toDataURL());
// 得到buffer
console.log(canvas.toBuffer());
```
### 保存二维码到本地相册
必须在导出临时文件路径成功后再执行保存。uni-app通用保存方式(H5除外):
```javascript
uni.saveImageToPhotosAlbum({
filePath: tempFilePath,
success: res => {
console.log(res);
},
fail: err => {
console.log(err);
}
});
```
H5可以通过设置`<a>`标签`href`属性的方式进行保存:
```javascript
const aEle = document.createElement('a');
aEle.download = 'uQRCode'; // 设置下载的文件名,默认是'下载'
aEle.href = tempFilePath;
document.body.appendChild(aEle);
aEle.click();
aEle.remove(); // 下载之后把创建的元素删除
```
经过测试,PC端浏览器可以下载,部分安卓自带或第三方浏览器可以下载,安卓微信浏览器不适用,移动端iOS所有浏览器均不适用,差异较大,还是推荐各位导出文件给图片组件显示,然后提示用户通过长按图片进行保存这种方式。
## uni-app组件方式
### 安装
通过uni-app插件市场地址安装:[https://ext.dcloud.net.cn/plugin?id=1287](https://ext.dcloud.net.cn/plugin?id=1287)。详细配置请移步到:文档 > [uni-app组件](https://doc.uqrcode.cn/document/uni-app.html)。
### 引入
uni-app默认为easycom模式,可直接键入`<uqrcode>`标签。
### 简单用法
安装`uqrcode`组件后,在`template`中键入`<uqrcode/>`。设置`ref`属性可使用组件内部方法,`canvas-id`属性为组件内部的canvas组件标识,`value`属性为二维码生成对应内容,`options`为配置选项,可配置二维码样式,绘制Logo等,详见:[options](https://doc.uqrcode.cn/document/uni-app.html#options) 。
``` html
<uqrcode ref="uqrcode" canvas-id="qrcode" value="https://doc.uqrcode.cn" :options="{ margin: 10 }"></uqrcode>
```
### 导出临时文件路径
为了保证方法调用成功,请在 [complete](https://doc.uqrcode.cn/document/uni-app.html#complete) 事件返回`success=true`后调用。
```javascript
// uqrcode为组件的ref名称
this.$refs.uqrcode.toTempFilePath({
success: res => {
console.log(res);
}
});
```
### 保存二维码到本地相册
为了保证方法调用成功,请在 [complete](https://doc.uqrcode.cn/document/uni-app.html#complete) 事件返回`success=true`后调用。
```javascript
// uqrcode为组件的ref名称
this.$refs.uqrcode.save({
success: () => {
uni.showToast({
icon: 'success',
title: '保存成功'
});
}
});
```
## 更多使用说明请前往官方文档查看:[https://doc.uqrcode.cn](https://doc.uqrcode.cn)。
+41
View File
@@ -0,0 +1,41 @@
## 1.0.82025-06-05
##### 优化内容
- 新增按照指定属性和值删除覆盖物
- 新增覆盖物带箭头折线覆盖物、带箭头三次曲线覆盖物、不带箭头三次曲线覆盖物
## 1.0.72025-04-29
##### 优化内容
- 修改拖拽结束事件等待惯性结束后触发事件
- 修改缩放事件结束后,返回`lon` `lat` `zoom`
## 1.0.62025-04-29
##### bug修复
- 修复方法setOption同时配置 zoom、center时,center不生效问题
- 修复覆盖物infowindow设置offsetX、offsetY无效问题
## 1.0.52025-04-23
##### 新增内容
- 支持覆盖物点击事件,详情见文档
- 支持启用/禁用地图拖拽、双击缩放、双指缩放、惯性拖拽,详情见文档
- 新增文本覆盖物、信息窗口,详情见文档
## 1.0.42025-04-11
##### 新增内容
- 地图新增覆盖物,详情见文档
- 标注添加默认图标
## 1.0.32025-03-30
##### Bug修复
- 修复对VUE2的兼容
## 1.0.22025-03-17
##### Bug修复
- 修复H5环境下的兼容性
- 修复对VUE3的兼容
- 修复控制台错误信息打印
## 1.0.12025-03-14
##### 新增内容
- 新增地图缩放事件
- 新增多数属性同时设置接口
- 新增可修改中心图标
##### 优化内容
- 优化地图拖拽事件回调中心位置
- 优化标注可设置位置偏移
- 优化交互
## 1.0.02025-03-12
首发
@@ -0,0 +1,662 @@
<template>
<view class="ly-map-wrapper">
<!-- 地图展示 -->
<view :id="mapId" :config="config" :change:config="LyMap.init" :call="option" :change:call="LyMap.call"
class="ly-map" />
<!-- 中心图标 -->
<image v-if="showCenterIcon" :src="centerIcon||defCenterIcon" class="ly-center-icon" />
<!-- 定位图标 -->
<view v-if="showLocationIcon" class="ly-location-icon" @click="onLocation">
<image :src="locationIcon" class="icon" />
</view>
</view>
</template>
<script>
import centerIcon from '../../static/ly-map/center.png';
import locationIcon from '../../static/ly-map/location.png';
export default {
// 接口参数
props: {
// 地图key
mapKey: {
type: String,
default: ''
},
// 经纬度
lonlat: {
type: Array,
default: () => ([111.668097, 40.825417]),
},
// 缩放
zoom: {
type: Number,
default: 16,
},
// 是否显示中心定位图标
showCenterIcon: {
type: Boolean,
default: false,
},
// 中心点图标
centerIcon: {
type: String,
default: "",
},
// 是否显示用户定位图标
showLocationIcon: {
type: Boolean,
default: false,
}
},
// 数据定义
data() {
return {
// 地图容器id
mapId: this.genId(),
// 调用渲染层
option: {},
// 地图配置(传递到render中的数据)
config: {},
// 调用渲染层队列
event: [],
// 事件处理定时器
timer: null,
// 中心图标
defCenterIcon: centerIcon,
// 定位图标
locationIcon: locationIcon,
}
},
// 挂载完成后
mounted() {
// 设置渲染数据
this.config = {
mapId: this.mapId,
mapKey: this.mapKey,
lonlat: this.lonlat,
zoom: this.zoom,
};
},
// 通用方法
methods: {
// 生成唯一ID
genId() {
let result = '';
const characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
const charactersLength = characters.length;
for (let i = 0; i < 30; i++) {
result += characters.charAt(Math.floor(Math.random() * charactersLength));
}
return Date.now() + result;
},
// 调用
call() {
if (this.timer) return;
// 消费事件队列(生产者/消费者机制)
this.timer = setInterval(() => {
if (this.event.length) {
this.option = this.event.shift();
} else {
clearInterval(this.timer);
this.timer = null;
}
}, 10);
},
// 添加事件队列
addEvent(name, data) {
// #ifdef APP-PLUS
// tips:由于采用监听option改变来调用方法,
// 如果连续变化两次option,渲染层只会则监听到最后一次
// 导致调用丢失,所以采用事件队列形式解决,稍微延时10ms
// 等待渲染进程监听到option变化,在进行更改option
// 从性能上,几乎无感可以放心使用
const option = {
id: this.genId(),
name: `_${name}`,
data
};
this.event.push(option);
this.call();
// #endif
// #ifndef APP-PLUS
this[`_${name}`] && this[`_${name}`](data);
// #endif
},
// 设置
setOption(option) {
this.addEvent("setOption", option);
},
// 设置类型 0矢量地图 1卫星地图
geoCoder(lng,lat,uuid) {
this.addEvent("geoCoder", {
lng,lat,uuid
});
},
// 设置位置
setCenter(lon, lat, zoom) {
this.addEvent("setCenter", {
lon,
lat,
zoom
});
},
// 设置类型 0矢量地图 1卫星地图
setType(type) {
this.addEvent("setType", {
type
});
},
// 设置标注 marks:[{url, width, height, lon, lat}]
setMarkers(markers = []) {
this.addEvent("setMarkers", {
markers
});
},
// 在原有的情况下添加覆盖物
addOvers(overs) {
this.addEvent("addOvers", overs);
},
// 清除原有同类型的覆盖物,并重新创建
setOvers(overs) {
this.addEvent("setOvers", overs);
},
// 移除指定属性的覆盖物
removeOvers(prop, value) {
this.addEvent("removeOvers", {
prop,
value
});
},
// 清除指定类型的覆盖物,不指定则清除所有
clearOvers(type = "") {
this.addEvent("clearOvers", type);
},
// 设置缩放
setZoom(zoom) {
this.addEvent("setZoom", {
zoom
});
},
// 设置是使能
setEnable(option) {
this.addEvent("setEnable", option);
},
// 重置地图
resize() {
this.addEvent("resize");
},
// 开始拖拽地图
UserEvent(data) {
this.$emit('onUserEvent',data);
}
},
}
</script>
<script module="LyMap" lang="renderjs">
// 天地图接口
const TDT_API = "https://api.tianditu.gov.cn/api?v=4.0&tk=";
export default {
data() {
return {
// 初始化配置数据
_config: {},
// 地图实例
_mapInstance: {},
// 注记
_markerList: [],
// 覆盖物
_overs: {},
// 地图事件
_event: {},
};
},
methods: {
// 初始化
init(newValue, oldValue, ownerInstance, instance) {
// 防止重复渲染
if (!Object.keys(newValue).length) {
return;
}
this._config = newValue;
// 初始化地图
if (typeof window.LyMap === 'function') {
this._createMap();
} else {
const script = document.createElement('script');
script.src = TDT_API + newValue.mapKey;
script.onload = this._createMap;
document.head.appendChild(script);
}
},
// 通过监听call来调用渲染层方法
call(newValue, oldValue, ownerInstance, instance) {
if (this[newValue.name] && typeof this[newValue.name] === "function") {
this[newValue.name](newValue.data);
}
},
// 创建地图
_createMap() {
// 创建地图实例
try {
this._mapInstance = new T.Map(this._config.mapId);
this._mapInstance.addEventListener('load', () => {
this.$ownerInstance.callMethod('UserEvent',{type:'onMapLoad'});
});
setTimeout(() => {
this._setCenter({
lon: this._config.lonlat[0],
lat: this._config.lonlat[1],
zoom: this._config.zoom
});
}, 100);
this._bindEvent();
} catch (err) {
console.error(err);
}
},
_geoCoder(lng,lat, uuid) {
//创建对象
const geocode = new T.Geocoder();
geocode.getLocation(new T.LngLat(lng,lat), (res)=>{
console.log(res.getStatus ());
console.log(res.getMsg ());
console.log(res.getAddress ());
console.log(res.getAddressComponent ());
console.log(res.getLocationLevel ());
});
return geocode;
},
// 绑定事件
_bindEvent() {
try {
// 绑定开始移动事件
this._event = {};
var fn = ({type})=>{
if(this._mapInstance==null)return;
const center = this._mapInstance.getCenter();
this.$ownerInstance.callMethod('UserEvent', {
type,
lng: center.getLng(),
lat: center.getLat()
});
}
//this._mapInstance.addEventListener('movestart', fn);
this._mapInstance.addEventListener('move', fn);
this._mapInstance.addEventListener('moveend', fn);
this._mapInstance.addEventListener('zoomstart', fn);
this._mapInstance.addEventListener('zoomend', fn);
this._mapInstance.addEventListener('dragstart', fn);
this._mapInstance.addEventListener('drag', fn);
this._mapInstance.addEventListener('dragend', fn);
this._mapInstance.addEventListener('touchstart', fn);
this._mapInstance.addEventListener('touchmove', fn);
this._mapInstance.addEventListener('touchend', fn);
this._mapInstance.addEventListener('longpress', fn);
} catch (err) {
console.error(err);
}
},
// 卸载事件
_unbindEvent() {
const eventsKey = Object.keys(this._event);
eventsKey.forEach(key => this._mapInstance.removeEventListener(key));
this._event = null;
},
// 重置地图尺寸
_resize() {
try {
this._mapInstance.checkResize && this._mapInstance.checkResize();
} catch (err) {
console.error(err);
}
},
// 设置选项
_setOption(data) {
data.type && this._setType(data);
data.center && this._setCenter({
...data.center,
zoom: data.zoom
});
if (!data.center && data.zoom) this._setZoom(data);
data.markers && this._setMarkers(data);
data.overs && this.setOvers(data.overs);
},
// 设置中心点
_setCenter(data) {
try {
this._resize();
this._mapInstance.panTo(new T.LngLat(Number(data.lon), Number(data.lat)), data.zoom || this._mapInstance.getZoom());
} catch (err) {
console.error(err);
}
},
// 设置地图显示类型
_setType(data) {
try {
this._mapInstance.setMapType(data.type === 1 ? TMAP_SATELLITE_MAP : TMAP_NORMAL_MAP);
} catch (err) {
console.error(err);
}
},
// 设置地图缩放
_setZoom(data) {
try {
this._mapInstance.setZoom(data.zoom);
} catch (err) {
console.error(err);
}
},
// 设置注记
_setMarkers(data) {
try {
// 清空当前所有注记
//if (!this._markerList) this._markerList = [];
//this._markerList.forEach(marker => this._mapInstance.removeOverLay(marker));
this._markerList = [];
this._mapInstance.clearOverLays();
// 创建新注记
data.markers && data.markers.forEach(item => {
console.log(item)
const option = {};
if (item.icon) {
option.icon = new T.Icon({
iconUrl: item.icon,
iconSize: new T.Point(item.width || 24, item.height || 24),
iconAnchor: new T.Point(item.offsetX || item.width / 2, item.offsetY ||
item.height)
});
}
const marker = new T.Marker(new T.LngLat(item.lon, item.lat), option);
item.click && marker.addEventListener("click", () => this._setEvent('click', item));
this._markerList.push(marker);
this._mapInstance.addOverLay(marker);
});
} catch (err) {
console.error(err);
}
},
// 设置使能
_setEnable(data) {
// 允许地图拖拽
if (data.hasOwnProperty("drag")) {
!!data["drag"] ? this._mapInstance.enableDrag() : this._mapInstance.disableDrag();
}
// 允许双击放大,默认启用。
if (data.hasOwnProperty("doubleClickZoom")) {
!!data["doubleClickZoom"] ? this._mapInstance.enableDoubleClickZoom() : this._mapInstance
.disableDoubleClickZoom();
}
// 允许地图惯性拖拽
if (data.hasOwnProperty("inertia")) {
!!data["inertia"] ? this._mapInstance.enableInertia() : this._mapInstance.disableInertia();
}
// 允许双指操作缩放
if (data.hasOwnProperty("pinchToZoom")) {
!!data["pinchToZoom"] ? this._mapInstance.enablePinchToZoom() : this._mapInstance.disablePinchToZoom();
}
},
// 添加覆盖物
_addOvers(data = []) {
if (!this._overs) this._overs = {};
data.forEach(item => {
switch (item.type) {
// 信息窗口
case "infowindow":
this._createOverInfoWindow(item);
break;
// 文本
case "label":
this._createOverLabel(item);
break;
// 创建圆
case "circle":
this._createOverCircle(item);
break;
// 创建折线
case "polyline":
this._createOverPolyLine(item);
break;
// 创建多边形
case "polygon":
this._createOverPolygon(item);
break;
// 创建矩形
case "rect":
this._createOverRectangle(item);
break;
// 创建带箭头的折线
case "polylineArrow":
// 创建不带箭头的三次样条曲线
case "cardinalCurve":
// 创建带箭头的三次样条曲线
case "cardinalCurveArrow":
this._createOverSymbol(item);
break;
default:
console.warn(
`'${item.type}'未知的覆盖物类型,有效值为 circle,polyline,polygon,rect,polylineArrow,cardinalCurve,cardinalCurveArrow`
);
}
});
},
// 设置覆盖物
_setOvers(data = []) {
if (!this._overs) this._overs = {};
// 移除现有同类型(时间复杂度On^2,不影响使用,暂时不优化了)
data.forEach(item => this._clearOvers(item.type));
// 添加
this._addOvers(data);
},
// 清除覆盖物
_clearOvers(type = "") {
if (!this._overs) this._overs = {};
// 清除
for (const id in this._overs) {
if (this._overs[id].options.type === type || !type) {
this._mapInstance.removeOverLay(this._overs[id]);
delete this._overs[id];
}
}
},
// 移除指定属性的覆盖物
_removeOvers(data = {}) {
if (!this._overs) this._overs = {};
for (const id in this._overs) {
if (this._overs[id].options[data.prop] === data.value) {
this._mapInstance.removeOverLay(this._overs[id]);
delete this._overs[id];
}
}
},
// 创建覆盖物
_createOver(over) {
// 添加到覆盖物层
this._mapInstance.addOverLay(over);
this._overs[this._genKey()] = over;
// 绑定点击事件
if (over.options.click) {
over.addEventListener("click", () => this._setEvent('click', over.options));
}
},
// 创建信息窗口
_createOverInfoWindow(option) {
try {
// 格式化参数
const param = {
...option
};
// 弹出窗口位置的补偿值。在同一图层中打开弹出窗口时对于控制锚点比较有用。
if (option.offsetX !== undefined || option.offsetY !== undefined) {
param.offset = new T.Point(option.offsetX || 0, option.offsetY || 0);
}
// 在地图视图自动平移产生后弹出窗口和地图视图之间的边缘。
if (option.autoPanPaddingX !== undefined || option.autoPanPaddingY !== undefined) {
param.autoPanPadding = new T.Point(option.autoPanPaddingX || 0, option.autoPanPaddingY || 0);
}
const infoWin = new T.InfoWindow(option.content, param);
// 设置或改变信息浮窗所指向的地理位置坐标
if (option.lon && option.lat) {
infoWin.setLngLat(new T.LngLat(option.lon, option.lat));
}
this._createOver(infoWin);
} catch (err) {
console.error(err);
}
},
// 创建文本覆盖物
_createOverLabel(option) {
try {
// 格式化参数
const param = {
...option
};
if (option.lon && option.lat) param.position = new T.LngLat(option.lon, option.lat);
param.offset = new T.Point(option.offsetX || 0, option.offsetY || 0);
const label = new T.Label(param);
this._createOver(label);
} catch (err) {
console.error(err);
}
},
// 创建圆覆盖物
_createOverCircle(option) {
try {
const circle = new T.Circle(new T.LngLat(option.lon, option.lat), option.rad, option);
this._createOver(circle);
} catch (err) {
console.error(err);
}
},
// 创建折线覆盖物
_createOverPolyLine(option) {
try {
const points = option.points.map(point => new T.LngLat(point[0], point[1]));
const line = new T.Polyline(points, option);
this._createOver(line);
} catch (err) {
console.error(err);
}
},
// 创建多边形覆盖物
_createOverPolygon(option) {
try {
const points = option.points.map(point => new T.LngLat(point[0], point[1]));
const polygon = new T.Polygon(points, option);
this._createOver(polygon);
} catch (err) {
console.error(err);
}
},
// 创建矩形覆盖物
_createOverRectangle(option) {
try {
const lt = option.leftTop;
const rb = option.rightBottom;
const bounds = new T.LngLatBounds(new T.LngLat(lt[0], lt[1]), new T.LngLat(rb[0], rb[1]));
const rect = new T.Rectangle(bounds, option);
this._createOver(rect);
} catch (err) {
console.error(err);
}
},
// 创建符号覆盖物
_createOverSymbol(option) {
try {
let over = null;
const points = option.points.map(point => new T.LngLat(point[0], point[1]));
switch (option.type) {
case "polylineArrow":
over = new T.PolylineArrow(points, option);
break;
case "cardinalCurve":
over = new T.CardinalCurve(points, option);
break;
case "cardinalCurveArrow":
over = new T.CardinalCurveArrow(points, option);
break;
default:
break;
}
if (over) this._createOver(over);
} catch (err) {
console.error(err);
}
},
// 设置事件
_setEvent(type, target) {
this.$ownerInstance.callMethod('UserEvent', {
type:'onMapListen',
type, // 事件类型UserEvent
target, // 事件目标
});
},
// 释放地图
_dispose() {
delete this._mapInstance;
this._mapInstance = null;
this._unbindEvent();
},
// 生成唯一ID
_genKey() {
let result = '';
const characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
const charactersLength = characters.length;
for (let i = 0; i < 30; i++) {
result += characters.charAt(Math.floor(Math.random() * charactersLength));
}
return Date.now() + result;
},
},
};
</script>
<style lang="scss" scoped>
.ly-map-wrapper {
width: 100%;
height: 100%;
position: relative;
overflow: hidden;
background: #f0f0f0;
.ly-map {
width: 100%;
height: 100%;
}
.ly-center-icon {
position: absolute;
z-index: 401;
left: 50%;
top: 50%;
transform: translateX(-50%) translateY(-100%);
width: 64rpx;
height: 64rpx;
}
.ly-location-icon {
position: absolute;
z-index: 401;
right: 24rpx;
bottom: 24rpx;
width: 72rpx;
height: 72rpx;
background: #ffffff;
border-radius: 12rpx;
display: flex;
align-items: center;
justify-content: center;
box-shadow: 0 0 8rpx rgba(0, 0, 0, .15);
.icon {
width: 44rpx;
height: 44rpx;
}
}
}
</style>
+79
View File
@@ -0,0 +1,79 @@
{
"id": "ly-map",
"displayName": "天地图(支持安卓、苹果、鸿蒙、支持多地图、覆盖物,平替高德腾讯地图年省5万)",
"version": "1.0.8",
"description": "支持多地图同时使用,支持卫星地图展示、支持多标注展示,支持自定义事件处理、支持自定义样式,平替高德/百度/腾讯地图展示年省5万",
"keywords": [
"地图",
"天地图",
"地图标注",
"覆盖物"
],
"repository": "",
"engines": {
"HBuilderX": "^4.01"
},
"uni_modules": {
"dependencies": [],
"encrypt": [],
"platforms": {
"cloud": {
"tcb": "y",
"aliyun": "y",
"alipay": "y"
},
"client": {
"Vue": {
"vue2": "y",
"vue3": "y"
},
"App": {
"app-vue": "y",
"app-nvue": "n",
"app-uvue": "n",
"app-harmony": "u"
},
"H5-mobile": {
"Safari": "y",
"Android Browser": "y",
"微信浏览器(Android)": "y",
"QQ浏览器(Android)": "y"
},
"H5-pc": {
"Chrome": "y",
"IE": "y",
"Edge": "y",
"Firefox": "y",
"Safari": "y"
},
"小程序": {
"微信": "n",
"阿里": "n",
"百度": "n",
"字节跳动": "n",
"QQ": "n",
"钉钉": "n",
"快手": "n",
"飞书": "n",
"京东": "n"
},
"快应用": {
"华为": "n",
"联盟": "n"
}
}
}
},
"name": "天地图(支持Android、IOS、Harmony、支持多地图、覆盖物,平替高德腾讯地图年省5万)",
"dcloudext": {
"contact": {
"qq": ""
},
"declaration": {
"ads": "无",
"data": "无",
"permissions": "无"
},
"npmurl": ""
}
}
+615
View File
@@ -0,0 +1,615 @@
### 地图组件 ly-map
<hr>
### 1 坐标系说明
#### 默认坐标系
```
天地图为WGS84坐标系,如果当前使用的是百度坐标(BD09)、火星坐标(GCJ02)
则需要转化后使用,推荐使用coord-transform工具转换
```
#### 转换工具安装
```
// npm
npm install @isvend/coord-transform
// pnpm
pnpm add @isvend/coord-transform
```
#### 坐标转换
```javascript
import {
transformWGS84ToBD09,
transformBD09ToGCJ02,
transformBD09ToWGS84,
transformGCJ02ToWGS84,
transformWGS84ToGCJ02,
} from "@isvend/coord-transform";
const lng = 116.404;
const lat = 39.915;
console.log("WGS84 to GCJ02", transformWGS84ToGCJ02(lng, lat));
console.log("GCJ02 to WGS84", transformGCJ02ToWGS84(lng, lat));
console.log("WGS84 to BD09", transformWGS84ToBD09(lng, lat));
console.log("BD09 to WGS84", transformBD09ToWGS84(lng, lat));
console.log("BD09 to GCJ02", transformBD09ToGCJ02(lng, lat));
console.log("GCJ02 to BD09", transformBD09ToGCJ02(lng, lat));
```
<hr>
### 2 初始化
默认填充父布局
```vue
<ly-map ref="map" map-key="天地图key"/>
```
<hr>
### 3 插件说明
##### 属性
| 参数 | 类型 | 说明 |
|:-------------------|:---------:|:----------------------------------|
| `mapKey` | `String` | 天地图官网申请的key,必填否则地图不能正确出来 |
| `lonlat` | `Array` | 地图初始位置,默认值[111.668097, 40.825417] |
| `zoom` | `Number` | 地图初始缩放等级,默认值16 |
| `showCenterIcon` | `Boolean` | 是否在地图中间显示图标, 默认值 false不显示 |
| `centerIcon` | `String` | 修改默认中间显示图标 |
| `showLocationIcon` | `Boolean` | 是否在地图右下角显示定位图标, 默认值 false不显示 |
##### 事件
| 事件 | 说明 |
|:-------------------------------------- |:------------------------------------------------------- |
| `onLoaded() ` | 当地图加载完成时 |
| `onLocation() ` | 点击右下角定位按钮时 |
| `onZoomEnd(lon:number, lat:number, zoom:number) ` | 缩放完成时,并返回中心位置以及当前缩放等级 |
| `onStartDrag(lon:number, lat:number)` | 当地图开始拖拽时,并返回中心位置 |
| `onEndDrag(lon:number, lat:number) ` | 当地图结束拖拽时,并返回中心位置 |
| `onListen(event:object) ` | 覆盖物事件监听,需要覆盖物属性`click``true`可被触发,目前支持点击事件 `event`说明如下 |
##### 覆盖物事件 `Event` 格式说明
| key | value | 说明 |
|:---------|:--------:|:----------------------------------------------|
| `type` | `String` | 事件类型 支持 `click` |
| `target` | `Object` | 发生事件的覆盖物option,会原样返回覆盖物的配置格式`Over``Marker` |
##### 方法
| 方法 | 说明 |
|:------------------------------------------------|:----------------------------------------------------------------------------------------------------------------|
| `setCenter(lon:number, lat:number,zoom:Number)` | 将地图的中心点变换到指定的地理坐标,如果同时指定了缩放等级,则同时缩放到指定的等级<br/>参数说明:<br/> `lon`:地图的中心点经度<br/> `lat`:地图的中心点纬度<br/>`zoom`:地图的比例尺级别 |
| `setType(type:Number) ` | 设置地图显示类型<br/>参数说明:<br/> `type`:要设置的地图展示类型 0矢量地图 1卫星地图 其它显示矢量地图 |
| `setMarkers(Array<Marker>) ` | 将标注添加到地图中,支持多个标注同时添加,标注`Marker`格式见下说明 |
| `setZoom(zoom:Number) ` | 将视图缩放到指定的缩放等级,中心点坐标不变 <br/>参数说明:<br/> `zoom`:地图的比例尺级别 |
| `resize() ` | 重置地图展示区域尺寸 |
| `setOption(option:Object) ` | 同时设置多个属性,支持中心点、缩放、标注、覆盖物<br/>参数说明:<br/> `Object`格式见下说明 |
| `addOvers(overs:Array<Over>) ` | 添加覆盖物,同时设置多个(会在现有的基础上添加)<br/>参数说明:<br/> `Over`格式见下说明 |
| `setOvers(overs:Array<Over>) ` | 设置覆盖物,同时设置多个(清除现有同类型的,重新添加)<br/>参数说明:<br/> `Over`格式见下说明 |
| `clearOvers(type:String) ` | 清除指定的覆盖物类型,不指定则清除所有<br/>参数说明:<br/> type=`circle``polyline``polygon``rect``label``infowindow` |
| `removeOvers(prop, value) ` | 移除指定属性和值的覆盖物,匹配创建覆盖物时的prop和value <br/>参数说明:<br/> `prop`:覆盖物属性 <br/> `value`:覆盖物属性对应的值 |
| `setEnable(option:Object) ` | 设置开启或者禁用的地图操作 支持拖拽、双击缩放、双指缩放、惯性拖拽 <br/> `Option`格式见下说明 |
##### 标注 `Marker` 格式说明
| key | value | 说明 |
|:-----------|:---------:|:------------------------------|
| `icon` | `String` | 标注显示图标的路径,支持网络图片 |
| `width` | `Number` | 标注图标宽度,注意:不设置icon时无效 |
| `height` | `Number` | 标注图标高度,注意:不设置icon时无效 |
| `lon ` | `Number` | 标注显示的位置经度 |
| `lat ` | `Number` | 标注显示的位置纬度 |
| `offsetX` | `Number` | 标注图标相对左上角水平偏移 ,注意:不设置icon时无效 |
| `offsetY` | `Number` | 标注图标相对左上角垂直偏移 ,注意:不设置icon时无效 |
##### 覆盖物 `Over`格式说明
| key | value | 说明 |
|:--------------- |:------------------: |:------------------------------------------------------------------------- |
| `type` | `String` | 覆盖物类型 支持 `circle` `polyline` `polygon` `rect` `label` `infowindow` `polylineArrow` `cardinalCurve` `cardinalCurveArrow` |
| `color` | `String` | 边线颜色 |
| `weight` | `Number` | 边线的宽度,以像素为单位 |
| `opacity ` | `Number` | 边线的透明度(范围0-1 之间) |
| `fillColor ` | `String` | 填充颜色。当参数为空时,折线覆盖物将没有填充效果 |
| `fillOpacity` | `Number` | 填充的透明度(范围0-1 之间)。 |
| `lineStyle` | `String` | 边线的样式(`solid``dashed`)注意:当`polylineArrow` `cardinalCurve` `cardinalCurveArrow`时,不生效(天地图缺陷) |
| `rad` | `Number` | `type=circle`时有效,圆的半径单位`m` |
| `lon ` | `Number` | `type=circle`时圆心经纬度坐标;`type=label`文本标注的地理位置;`type=infowindow`弹窗的显示地理位置坐标 |
| `lat ` | `Number` | `type=circle`时圆心经纬度坐标;`type=label`文本标注的地理位置;`type=infowindow`弹窗的显示地理位置坐标 |
| `points` | `Array<[lon,lat]>` | `type=polygon或polyline`时有效,坐标数组 |
| `leftTop` | `Array<[lon,lat]>` | `type=rect`时有效, 矩形左上角坐标 |
| `rightBottom` | `Array<[lon,lat]>` | `type=rect`时有效, 矩形右下角坐标 |
| `text` | `String` | `type=label`时有效,显示文本内容; |
| `offsetX` | `Number` | `type=label`时有效,显示文本水平偏移;`type=infowindow`时有效,显示弹窗水平偏移 |
| `offsetY` | `Number` | `type=label`时有效,显示文垂直平偏移;`type=infowindow`时有效,显示弹窗垂直偏移 |
| `minWidth` | `Number` | `type=infowindow`时有效,弹出框的最小宽度 |
| `maxWidth` | `Number` | `type=infowindow`时有效,弹出框的最大宽度 |
| `maxHeight` | `Number` | `type=infowindow`时有效,设置后,如果内容超过弹出窗口的给定高度则产生一个可以滚动的容器 |
| `autoPan` | `Boolean` | `type=infowindow`时有效,否开启信息窗口打开时地图自动移动(默认关闭) |
| `closeButton` | `Boolean` | `type=infowindow`时有效,控制弹出窗口中出现的关闭按钮 |
| `closeOnClick` | `Boolean` | `type=infowindow`时有效,是否开启点击地图关闭信息窗口(默认关闭) |
##### 地图操作禁用配置 `Option` 格式说明
| key | value | 说明 |
|:------------------|:---------:|:-------------------------------|
| `drag` | `Boolean` | 地图拖拽 `true`允许 `fale`禁用,默认启用 |
| `doubleClickZoom` | `Boolean` | 地图双击放大`true`允许 `fale`禁用,默认启用 |
| `pinchToZoom` | `Boolean` | 双指操作缩放 `true`允许 `fale`禁用,默认启用 |
| `inertia ` | `Boolean` | 地图惯性拖拽 `true`允许 `fale`禁用,默认启用 | |
##### 多属性 `Object` 配置说明
```javascript
const option = {
// 中心点
center: {lon: 116.391231, lat: 39.907334},
// 多标注
markers: [{
icon: icon,
width: 24,
height: 27,
lon: 116.389231,
lat: 39.906034,
offsetX: 12,
offsetY: 27,
click: true, // 开启点击监听
}],
// 缩放
zoom: 16,
// 地图类型
type: 1,
// 覆盖物
overs: [
// 添加圆
{
type: "circle",
rad: 100,
lon: 111.667920,
lat: 40.828050,
color: "#0000FF",
weight: 3,
opacity: .5,
fillColor: "#0000FF",
fillOpacity: .2,
lineStyle: "solid",
},
// 添加折线
{
type: "polyline",
points: [[111.665750, 40.822830], [111.668190, 40.821800], [111.670310, 40.822790]],
color: "#0000FF",
weight: 3,
opacity: .5,
lineStyle: "solid",
},
// 添加多边形
{
type: "polygon",
points: [[111.668160, 40.824850], [111.666640, 40.823950], [111.669570, 40.823990]],
color: "#0000FF",
weight: 3,
opacity: .5,
lineStyle: "solid",
},
// 添加矩形
{
type: "rect",
leftTop: [111.665880, 40.826710],
rightBottom: [111.670120, 40.825860],
color: "#0000FF",
weight: 3,
opacity: .5,
lineStyle: "solid",
},
// 添加文本
{
type: "label",
lon: 111.668150,
lat: 40.825330,
text: "点我打开弹窗",
offsetX: -60,
offsetY: 10,
click: true,
},
],
}
```
```text
注意:目前所有覆盖物都支持click属性,当click=true时,则开启点击监听,默认不开启
```
<hr>
### 4 示例代码
```vue
<template>
<view class="map-page-wrapper">
<view class="map-box">
<ly-map ref="map" :map-key="mapKey" :lonlat="lonlat" :zoom="zoom" :show-center-icon="showCenterIcon"
:center-icon="centerIcon" :show-location-icon="showLocationIcon" :on-location="location"
:on-loaded="loaded" :on-start-drag="startDrag" :on-zoom-end="zoomEnd" :on-end-drag="endDrag"
:on-listen="listen" />
</view>
<view class="tool-box">
<view class="tool" @tap="setType(0)">矢量地图</view>
<view class="tool" @tap="setType(1)">卫星地图</view>
<view class="tool" @tap="setMarkers()">设置标注</view>
<view class="tool" @tap="clearMarkers()">清除标注</view>
<view class="tool" @tap="setZoom(1)">缩放+</view>
<view class="tool" @tap="setZoom(-1)">缩放-</view>
<view class="tool" @tap="setCenter(116.391231,39.907334)">设置位置1</view>
<view class="tool" @tap="setCenter(111.668097, 40.825417)">设置位置2</view>
<view class="tool" @tap="setCenterIcon(true)">显示中心图标</view>
<view class="tool" @tap="setCenterIcon(false)">隐藏中心图标</view>
<view class="tool" @tap="setLocationIcon(true)">显示定位图标</view>
<view class="tool" @tap="setLocationIcon(false)">隐藏定位图标</view>
<view class="tool" @tap="setIcon()">修改中心图标</view>
<view class="tool" @tap="setOption()">同时设置属性</view>
<view class="tool" @tap="setOvers()">设置覆盖物</view>
<view class="tool" @tap="addOvers()">添加覆盖物</view>
<view class="tool" @tap="clearOvers('rect')">清除矩形覆盖物</view>
<view class="tool" @tap="clearOvers()">清除所有覆盖物</view>
<view class="tool" @tap="enable()">启用拖拽/缩放</view>
<view class="tool" @tap="disable()">禁用拖拽/缩放</view>
<view class="tool" @tap="openDialog()">打开弹窗</view>
<view class="tool" @tap="removeOvers()">移除指定属性覆盖物</view>
</view>
</view>
</template>
<script>
import icon1 from '@/static/icon1.png';
export default {
data() {
return {
// 天地图官网地址 https://oauth.tianditu.gov.cn/
// key需要自行申请,以下使用测试key
mapKey: "bcd1064810812bdc35d675d1dba3c23f",
// 中心位置
lonlat: [111.668097, 40.825417],
// 缩放
zoom: 16,
// 地图展示类型
type: 0,
// 显示中心图标
showCenterIcon: true,
// 显示定位图标
showLocationIcon: true,
// 不设置使用默认图标
centerIcon: "",
};
},
onReady() {
// 绑定组件
this.map = this.$refs.map;
},
methods: {
location() {
console.log("点击定位按钮回调");
},
loaded() {
console.log("地图创建完成");
},
zoomEnd(lon, lat) {
console.log("地图缩放完成", lon, lat);
},
startDrag(lon, lat) {
console.log("开始地图拖拽", lon, lat);
},
endDrag(lon, lat) {
console.log("结束地图拖拽", lon, lat);
},
listen(e) {
console.log("覆盖物点击事件", e);
// 只有点击label可以显示弹窗
if (e.target.type === "label") {
this.map.setOvers([{
type: "infowindow",
lon: 111.668100,
lat: 40.825420,
content: "弹窗",
}]);
}
},
setZoom(zoom) {
this.zoom += zoom;
if (this.zoom <= 10) this.zoom = 10;
if (this.zoom >= 20) this.zoom = 20;
this.map.setZoom(this.zoom);
},
setType(type) {
// type 0矢量地图 1卫星地图 其他矢量地图
this.type = type;
this.map.setType(this.type);
},
setCenter(lon, lat) {
this.lonlat = [lon, lat];
this.map.setCenter(lon, lat);
},
setMarkers() {
this.map.setMarkers([{
lon: 111.666020,
lat: 40.82740
},
{
lon: 111.667020,
lat: 40.82740
},
{
lon: 111.665020,
lat: 40.82740
},
{
lon: 111.669020,
lat: 40.82740
},
{
lon: 111.670020,
lat: 40.82740
},
]);
},
removeOvers() {
this.map.removeOvers("flag", "my-circle");
},
clearMarkers() {
this.map.setMarkers([]);
},
setCenterIcon(show) {
this.showCenterIcon = show;
},
setLocationIcon(show) {
this.showLocationIcon = show;
},
setIcon() {
// 默认图标和icon切换显示
this.centerIcon = this.centerIcon ? "" : icon1;
},
setOption() {
// 同时设置多个属性
this.map.setOption({
center: {
lon: 116.391231,
lat: 39.907334
},
markers: [{
lon: 116.389231,
lat: 39.906034
},
{
lon: 116.390231,
lat: 39.906034
},
{
lon: 116.391231,
lat: 39.906034
},
{
lon: 116.392231,
lat: 39.906034
},
{
lon: 116.393231,
lat: 39.906034
},
],
zoom: 16,
type: 1,
});
},
setOvers() {
const overs = [{
type: "circle",
rad: 100,
lon: 111.667920,
lat: 40.828050,
color: "#0000FF",
weight: 3,
opacity: .5,
fillColor: "#0000FF",
fillOpacity: .2,
lineStyle: "solid",
click: true,
flag: "my-circle",
},
{
type: "polyline",
points: [
[111.665750, 40.822830],
[111.668190, 40.821800],
[111.670310, 40.822790]
],
color: "#0000FF",
weight: 3,
opacity: 1,
lineStyle: "dashed",
},
{
type: "polylineArrow",
points: [
[111.665750, 40.823030],
[111.668190, 40.822000],
[111.670310, 40.822990]
],
color: "#0000FF",
weight: 3,
opacity: 1,
lineStyle: "dashed",
},
{
type: "cardinalCurve",
points: [
[111.665750, 40.823430],
[111.668190, 40.822400],
[111.670310, 40.823390]
],
color: "#0000FF",
weight: 3,
opacity: 1,
lineStyle: "dashed",
},
{
type: "cardinalCurveArrow",
points: [
[111.665750, 40.823730],
[111.668190, 40.822700],
[111.670310, 40.823690]
],
color: "#0000FF",
weight: 3,
opacity: 1,
lineStyle: "dashed",
},
{
type: "polygon",
points: [
[111.668160, 40.824850],
[111.666640, 40.823950],
[111.669570, 40.823990]
],
color: "#0000FF",
weight: 3,
opacity: .5,
lineStyle: "solid",
},
{
type: "rect",
leftTop: [111.665880, 40.826710],
rightBottom: [111.670120, 40.825860],
color: "#0000FF",
weight: 3,
opacity: .5,
lineStyle: "solid",
},
{
type: "label",
lon: 111.668150,
lat: 40.825330,
text: "点我打开弹窗",
offsetX: -60,
offsetY: 10,
click: true,
},
];
this.map.setOvers(overs);
},
addOvers() {
const overs = [{
type: "circle",
rad: 200,
lon: 111.667920,
lat: 40.828050,
color: "#0000FF",
weight: 3,
opacity: .5,
fillColor: "#0000FF",
fillOpacity: .2,
lineStyle: "solid",
}, ];
this.map.addOvers(overs);
},
clearOvers(type) {
this.map.clearOvers(type);
},
enable() {
this.map.setEnable({
drag: true,
doubleClickZoom: true,
inertia: true,
pinchToZoom: true,
});
},
disable() {
this.map.setEnable({
drag: false,
doubleClickZoom: false,
inertia: false,
pinchToZoom: false,
});
},
openDialog() {
const content =
`
<div style="display:flex; flex-direction: column;align-items: center; font-size:16px;z-index:999999999;">
<img src='http://lbs.tianditu.gov.cn/images/logo.png' width='100' height='50'/>
<div style='margin-top:8px;'>天地图组件</div>
<div style='margin-top:8px;'>内蒙古灵越科技</div>
</div>
`;
this.map.setOvers([{
type: "infowindow",
lon: 111.668100,
lat: 40.825420,
content: content,
}]);
}
}
};
</script>
<style lang="scss">
page {
height: 100%;
}
.map-page-wrapper {
width: 100%;
height: 100%;
.map-box {
width: 100%;
height: 100%;
}
.tool-box {
position: absolute;
// 天地图默认z-index:400 需要大于400
z-index: 999;
top: 0;
left: 0;
width: 100%;
border-radius: 8rpx;
padding: 16rpx;
display: flex;
flex-wrap: wrap;
gap: 16rpx;
.tool {
font-size: 28rpx;
border-radius: 8rpx;
padding: 12rpx 24rpx;
background: #ffffff;
box-shadow: 0 0 6rpx rgba(0, 0, 0, .15);
}
}
}
</style>
```
<hr>
### 5 QA
#### 为什么自定义标注图标在APP上会失效?
```
标注图标支持网络图片以及base64格式图标
```
Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

+189 -172
View File
@@ -51,10 +51,192 @@ isSet=(v) =>{
isWeakMap=(v) =>{
return v instanceof WeakMap;
},
isWeakSet=(v) =>{
isWeakSet=(v) => {
return v instanceof WeakSet;
},
_goto = (url,type) => {
//console.log(url);
type = type || '0'; //0 navigateTo 1 redirectTo
if(url){
if(isInteger(url)){
uni.navigateBack({
delta:url,
})
}else{
url+="";
if(url.substr(0,6) != '/pages'){
url='/pages'+url
}
if(type == '1'){
return uni.redirectTo({
url:url
});
}
if(type == '2'){
return uni.switchTab({
url:url
});
}
uni.navigateTo({
url:url
})
}
}
};
const showToast = (msg,url,icon) => {
//msg = i18n.t(msg);
// #ifdef APP
plus.nativeUI.closeToast();
plus.nativeUI.toast(msg,{
align:'center',
verticalAlign:"center",
style:"inline",
icon:icon=='error' ? '/static/img/common/error.png' : '/static/img/common/success.png',
iconWidth:24,
iconHeight:24
});
if(url){
setTimeout(()=>{
_goto(url);
},3000)
}
// #endif
// #ifndef APP
uni.showToast({
//image:icon=='error' ? '/static/img/common/error.png' : '/static/img/common/success.png',
icon:icon=='error' ? icon : 'success',
title:msg,
showToast:3000,
complete:()=>{
if(url){
setTimeout(()=>{
_goto(url);
},3000)
}
}
});
// #endif
}
const error = (msg,url) => {
showToast(msg,url,'error');
}
const success = (msg,url) => {
showToast(msg,url,'success');
}
const scan = ()=>{
uni.scanCode({
success(res){
/**
* result 所扫码的内容
scanType 所扫码的类型 App、微信小程序、百度小程序、QQ小程序、京东小程序、支付宝小程序
charSet 所扫码的字符集 App、微信小程序、百度小程序(所扫码的字符集,仅支持 Android 系统)、QQ小程序、京东小程序
path 当所扫的码为当前应用的合法二维码时,会返回此字段,内容为二维码携带的 path。 微信小程序、QQ小程序、京东小程序
rawData 原始数据,base64 编码 微信小程序、QQ小程序、京东小程序、支付宝小程序
code 扫码所得数据 支付宝小程序
qrCode 扫描二维码时返回二维码数据 支付宝小程序
barCode 扫描条形码时返回条形码数据 支付宝小程序
imageChannel 来源 支付宝小程序
*/
if(res.result){
if(res.result.indexOf('blackcatp:/')){
uni.navigateTo({
url:res.result.substring(11)
})
}else{
success(res.result)
}
}
},
fail(res){
},
complete(res){
console.log(res)
}
});
}
const fileExsit = async(fn)=>{
return await new Promise((resolve) => {
plus.io.resolveLocalFileSystemURL("_doc/"+fn, function(entry) {
resolve(true);
}, function() {
resolve(false);
});
})
}
const downloadFile = (url, savepath, successCb, errorCb, progressCb) => {
const root_dir = "_doc/";
if (!url) {
errorCb && errorCb.call(this,new Error('empty url'));
return;
}
const startDownload = () => {
console.log(33333333333333,root_dir+savepath);
const task = plus.downloader.createDownload(url, { filename: root_dir+savepath, timeout: 2 }, function(d, status) {
console.log('completedCB');
console.log(d);
console.log(status);
if (status === 200) {
const local = d && d.filename ? d.filename : root_dir+savepath;
successCb && successCb.call(this,local);
} else {
errorCb && errorCb.call(this,new Error('download status ' + status));
}
});
task.addEventListener('statechanged', function(t, status) {
console.log('statechanged',d,status);
if (t.state === 3) {
var downloaded = t.downloadedSize || t.downloaded || 0;
var total = t.totalSize || t.total || 0;
var prog = 0;
if (total > 0) prog = Math.min(100, Math.floor(downloaded / total * 100));
progressCb && progressCb.call(this,prog);
}
});
try{
task.start();
plus.downloader.startAll();
}catch(e){
console.log('e',e);
}
plus.downloader.enumerate((downloads )=>{
for(var i =0;i<downloads.length;i++){
//downloads[i].abort();
console.log(downloads[i]);
}
})
};
// 目录不存在,尝试创建(针对 _doc/<conversationID> 结构)
const fns = savepath.split('/');
plus.io.requestFileSystem(plus.io.PRIVATE_DOC, function(fs) {
fs.root.getDirectory(fns[0], { create: true }, function(entry) {
startDownload();
}, function(e) {
// 创建失败也尝试下载,可能运行时会自动创建
startDownload();
});
}, function(e) {
startDownload();
});
}
const cacheFile = async (url, savepath, successCb, errorCb, progressCb) => {
//plus.downloader.clear();
//return ;
const coverExists = await fileExsit(savepath);
if(coverExists){
successCb && successCb.call(this,"_doc/"+savepath);
}else{
downloadFile(url, savepath, successCb, errorCb, progressCb)
}
}
export default{
fileExsit,
downloadFile,
cacheFile,
isString :isString,
isNumber :isNumber,
isInteger :isInteger,
@@ -88,110 +270,11 @@ export default{
}
return "";
},
goto(url,type){
//console.log(url);
type = type || '0'; //0 navigateTo 1 redirectTo
if(url){
if(isInteger(url)){
uni.navigateBack({
delta:url,
})
}else{
url+="";
if(url.substr(0,6) != '/pages'){
url='/pages'+url
}
if(type == '1'){
return uni.redirectTo({
url:url
});
}
if(type == '2'){
return uni.switchTab({
url:url
});
}
uni.navigateTo({
url:url
})
}
}
},
showToast(msg,url,icon){
const _this = this;
//msg = i18n.t(msg);
// #ifdef APP
plus.nativeUI.closeToast();
plus.nativeUI.toast(msg,{
align:'center',
verticalAlign:"center",
style:"inline",
icon:icon=='error' ? '/static/img/common/error.png' : '/static/img/common/success.png',
iconWidth:24,
iconHeight:24
});
if(url){
setTimeout(()=>{
_this.goto(url);
},3000)
}
// #endif
// #ifndef APP
uni.showToast({
//image:icon=='error' ? '/static/img/common/error.png' : '/static/img/common/success.png',
icon:icon=='error' ? icon : 'success',
title:msg,
showToast:3000,
complete:()=>{
if(url){
setTimeout(()=>{
_this.goto(url);
},3000)
}
}
});
// #endif
},
error(msg,url){
this.showToast(msg,url,'error');
},
success(msg,url){
this.showToast(msg,url,'success');
},
scan(){
uni.scanCode({
success(res){
/**
* result 所扫码的内容
scanType 所扫码的类型 App、微信小程序、百度小程序、QQ小程序、京东小程序、支付宝小程序
charSet 所扫码的字符集 App、微信小程序、百度小程序(所扫码的字符集,仅支持 Android 系统)、QQ小程序、京东小程序
path 当所扫的码为当前应用的合法二维码时,会返回此字段,内容为二维码携带的 path。 微信小程序、QQ小程序、京东小程序
rawData 原始数据,base64 编码 微信小程序、QQ小程序、京东小程序、支付宝小程序
code 扫码所得数据 支付宝小程序
qrCode 扫描二维码时返回二维码数据 支付宝小程序
barCode 扫描条形码时返回条形码数据 支付宝小程序
imageChannel 来源 支付宝小程序
*/
if(res.result){
if(res.result.indexOf('blackcatp:/')){
uni.navigateTo({
url:res.result.substring(11)
})
}else{
this.success(res.result)
}
}
},
fail(res){
},
complete(res){
console.log(res)
}
});
},
"goto":_goto,
showToast,
error,
success,
scan,
copy(v){
let that = this;
uni.setClipboardData({
@@ -225,71 +308,5 @@ export default{
},
imapi(method,data){
return IMSDK.asyncApi(method,IMSDK.uuid(),data);
},
async fileExsit(fn){
return await new Promise((resolve) => {
plus.io.resolveLocalFileSystemURL(fn, function(entry) { resolve(true); }, function() { resolve(false); });
})
},
downloadFile (url, savepath, successCb, errorCb, progressCb) {
if (!url) {
errorCb && errorCb(new Error('empty url'));
return;
}
const startDownload = () => {
try {
const task = plus.downloader.createDownload(url, { filename: savepath, timeout: 120000 }, function(d, status) {
if (status === 200) {
const local = d && d.filename ? d.filename : savepath;
successCb && successCb(local);
} else {
errorCb && errorCb(new Error('download status ' + status));
}
});
try {
if (task && typeof task.addEventListener === 'function') {
task.addEventListener('statechanged', function(t, status) {
if (t.state === 3) {
var downloaded = t.downloadedSize || t.downloaded || 0;
var total = t.totalSize || t.total || 0;
var prog = 0;
if (total > 0) prog = Math.min(100, Math.floor(downloaded / total * 100));
progressCb && progressCb(prog);
}
});
}
} catch (e) {
// ignore
}
task.start();
} catch (e) {
errorCb && errorCb(e);
}
};
// 确保父目录存在
try {
const parent = savepath.substring(0, savepath.lastIndexOf('/'));
plus.io.resolveLocalFileSystemURL(parent, function(entry) {
startDownload();
}, function() {
// 目录不存在,尝试创建(针对 _doc/<conversationID> 结构)
let rel = parent;
if (rel.indexOf('_doc/') === 0) rel = rel.replace(/^_doc\//, '');
plus.io.requestFileSystem(plus.io.PRIVATE_DOC, function(fs) {
fs.root.getDirectory(rel, { create: true }, function(entry) {
startDownload();
}, function(e) {
// 创建失败也尝试下载,可能运行时会自动创建
startDownload();
});
}, function(e) {
startDownload();
});
});
} catch (e) {
startDownload();
}
}
}
}