324 lines
8.8 KiB
Plaintext
324 lines
8.8 KiB
Plaintext
<template>
|
||
<!-- #ifdef APP-IOS || APP-ANDROID || APP-HARMONY-->
|
||
<!-- <web-view class="l-svg" ref="webRef" v-if="web" @error="error" @load="loaded" @message="message"
|
||
src="/uni_modules/lime-svg/hybrid/html/index.html?v=21"></web-view> -->
|
||
<!-- <l-svg-x class="l-svg" v-else :src="path" :color="color" @error="onError" @load="onLoad"
|
||
@click="$emit('click')"></l-svg-x> -->
|
||
<!-- <image class="l-svg-" v-bind="$attrs" v-if="color == '' && src.indexOf('<svg') != 0 && src.indexOf('data:image') != 0" :src="src" @error="onError" @load="onLoadImage" /> -->
|
||
<native-view class="l-svg" v-bind="$attrs" @init="onviewinit" @error="onError" @load="onLoad"></native-view>
|
||
<!-- #endif -->
|
||
<!-- #ifdef WEB -->
|
||
<view class="l-svg" v-if="src.startsWith('<svg')" v-html="src" :style="styles"></view>
|
||
<view class="l-svg" :class="{'is-inherit': isInherit}" v-else :style="styles">
|
||
<image class="l-svg-img" :src="src" @error="onError" @load="onLoad" />
|
||
</view>
|
||
<!-- #endif -->
|
||
<!-- #ifdef APP-HARMONY -->
|
||
<!-- <native-view class="l-svg" v-bind="$attrs" @init="onviewinit" @error="onError" @load="onLoad"></native-view> -->
|
||
<!-- #endif -->
|
||
<!-- #ifndef APP-IOS || APP-ANDROID || APP-HARMONY || WEB -->
|
||
<view class="l-svg" :class="{'is-inherit': isInherit}" :style="styles">
|
||
<image class="l-svg-img" :src="path" @error="onError" @load="onLoad"></image>
|
||
</view>
|
||
<!-- #endif -->
|
||
</template>
|
||
|
||
<script setup lang="uts">
|
||
/**
|
||
* Svg SVG组件
|
||
* @description 用于渲染SVG路径元素,支持动态颜色和继承属性
|
||
* <br>插件类型:LSvpComponentPublicInstance
|
||
* @tutorial https://ext.dcloud.net.cn/plugin?name=lime-svg
|
||
*
|
||
* @property {string} src SVG路径
|
||
* @property {string} color 路径颜色(默认:"currentColor")
|
||
* @property {boolean} web 是否启用Web优化模式(默认:false)
|
||
* @property {boolean} inherit 是否继承父级SVG属性(默认:true)
|
||
* @event {Function} load SVG路径加载完成时触发
|
||
* @event {Function} error SVG路径加载失败时触发
|
||
*/
|
||
|
||
import { LSvpProps } from './type'
|
||
// #ifndef UNI-APP-X && APP || WEB
|
||
import { pathToDataUrl, svgToDataUrl } from './utils'
|
||
// #endif
|
||
// #ifdef APP-ANDROID || APP-IOS
|
||
import { pathToDataUrl, svgToDataUrl } from './utils'
|
||
// #endif
|
||
// #ifdef APP
|
||
import { NativeImage } from "@/uni_modules/lime-svg";
|
||
let nativeImage : NativeImage | null = null
|
||
// #endif
|
||
|
||
|
||
const props = withDefaults(defineProps<LSvpProps>(), {
|
||
src: '',
|
||
color: '',
|
||
web: false,
|
||
inherit: false
|
||
})
|
||
|
||
const emit = defineEmits(['load', 'error'])
|
||
const path = ref(props.src)
|
||
const svgRef = ref<UniElement | null>(null)
|
||
// #ifndef APP-ANDROID || APP-IOS
|
||
const isInherit = computed(() : boolean => {
|
||
return props.color != ''
|
||
})
|
||
// #endif
|
||
|
||
|
||
const imageURL = ref('')
|
||
const formatUrl = (url : string, action : string) : string => {
|
||
if (url.indexOf(`'`) > 0) return `${action}("${url}")`
|
||
return `${action}('${url}')`
|
||
}
|
||
const styles = computed(() : Map<string, string> => {
|
||
const style = new Map<string, string>()
|
||
// #ifdef WEB
|
||
if (props.src != '' && !props.src.startsWith('<svg')) {
|
||
// style.set('--svg', formatUrl(props.src, 'url'))
|
||
style.set('--svg', formatUrl(imageURL.value ?? props.src, 'url'))
|
||
}
|
||
// #endif
|
||
// #ifndef APP || WEB
|
||
if (path.value != '') {
|
||
// style.set('--svg', formatUrl(props.src, 'url'))
|
||
style.set('--svg', formatUrl(path.value, 'url'))
|
||
}
|
||
// #endif
|
||
if (props.color != '') {
|
||
style.set('color', props.color)
|
||
}
|
||
return style
|
||
})
|
||
|
||
|
||
|
||
|
||
|
||
// #ifdef APP-ANDROID
|
||
const errorDetaill = new UniImageErrorEventDetail('加载失败')
|
||
const errorEvent = new UniImageErrorEvent('error', errorDetaill)
|
||
// #endif
|
||
// #ifndef APP-ANDROID
|
||
const errorDetaill = {
|
||
errMsg: '加载失败'
|
||
}
|
||
const errorEvent = {
|
||
type: 'error',
|
||
detaill: errorDetaill
|
||
}
|
||
// #endif
|
||
|
||
const onError = () => {
|
||
emit('error', errorEvent)
|
||
}
|
||
const onLoadImage = (e: UniImageLoadEvent) => {
|
||
emit('load', e)
|
||
}
|
||
const onLoad = (e : UniNativeViewEvent) => {
|
||
// #ifdef WEB
|
||
// @ts-ignore
|
||
imageURL.value = e.target.src
|
||
// #endif
|
||
// #ifdef APP-ANDROID
|
||
const detail = new ImageLoadEventDetail(512, 512)
|
||
const loadEvent = new UniImageLoadEvent('load', detail)
|
||
// #endif
|
||
// #ifndef APP-ANDROID
|
||
const detail = {
|
||
width: 512,
|
||
height: 512
|
||
}
|
||
const loadEvent = {
|
||
type: 'load',
|
||
detail
|
||
}
|
||
// #endif
|
||
emit('load', loadEvent)
|
||
}
|
||
// app
|
||
// #ifdef APP-ANDROID || APP-IOS
|
||
// const webRef = ref<UniWebViewElement | null>(null)
|
||
// const setSvgSrc = () => {
|
||
// if (path.value != '') {
|
||
// webRef.value?.evalJS(formatUrl(path.value, 'setSrc'));
|
||
// }
|
||
// }
|
||
// const setSvgColor = () => {
|
||
// if (props.color != '' && path.value != '') {
|
||
// webRef.value?.evalJS(`setStyle({"--color": "${props.color}"})`);
|
||
// }
|
||
// }
|
||
// const error = (_ : UniWebViewErrorEvent) => {
|
||
// emit('error', errorEvent)
|
||
// }
|
||
// const loaded = (_ : UniWebViewLoadEvent) => {
|
||
// watchEffect(() => {
|
||
// if (props.src == '' || !props.web) return
|
||
// if (props.src.startsWith('<svg')) {
|
||
// path.value = svgToDataUrl(props.src)
|
||
// setSvgSrc()
|
||
// setSvgColor()
|
||
// } else if (props.src.startsWith('/static')) {
|
||
// pathToDataUrl(props.src).then(res => {
|
||
// path.value = res;
|
||
// setSvgSrc()
|
||
// setSvgColor()
|
||
// }).catch(err => {
|
||
// emit('error', errorEvent)
|
||
// console.warn("[lime-svg]" + props.src + JSON.stringify(err))
|
||
// })
|
||
// } else {
|
||
// path.value = props.src
|
||
// setSvgSrc()
|
||
// setSvgColor()
|
||
// }
|
||
// })
|
||
// }
|
||
// const message = (event : UniWebViewMessageEvent) => {
|
||
// const data = UTSJSONObject.assign({}, event.detail.data[0] as UTSJSONObject); //event.detail.data[0] as UTSJSONObject
|
||
// const type = data.getString('event')
|
||
// // #ifdef APP-ANDROID
|
||
// const detail = data.getJSON('data')?.getJSON('detail')
|
||
// // #endif
|
||
// // #ifndef APP-ANDROID
|
||
// const detail = UTSJSONObject.assign({}, data?.data?.detail ?? {})
|
||
// // #endif
|
||
// if (type == 'click') {
|
||
// emit('click')
|
||
// } else if (type == 'load') {
|
||
// const width = detail?.getNumber('width') ?? 512
|
||
// const height = detail?.getNumber('height') ?? 512
|
||
// // #ifdef APP-ANDROID
|
||
// const loadDetail = new ImageLoadEventDetail(width, height)
|
||
// const loadEvent = new UniImageLoadEvent('load', loadDetail)
|
||
// // #endif
|
||
// // #ifndef APP-ANDROID
|
||
// const loadDetail = {
|
||
// width,
|
||
// height
|
||
// }
|
||
// const loadEvent = {
|
||
// type: 'error',
|
||
// detail: loadDetail
|
||
// }
|
||
// // #endif
|
||
// emit(type, loadEvent)
|
||
// } else if (type == 'error') {
|
||
// emit(type, errorEvent)
|
||
// }
|
||
// }
|
||
// #endif
|
||
|
||
|
||
// #ifdef APP
|
||
function onviewinit(e : UniNativeViewInitEvent) {
|
||
nativeImage = new NativeImage(e.detail.element);
|
||
nativeImage?.updateSrc(path.value)
|
||
nativeImage?.updateColor(props.color)
|
||
}
|
||
const map = new Map<string, string>()
|
||
watchEffect(() => {
|
||
// #ifdef APP-ANDROID || APP-IOS
|
||
// ios uts组件使用uni.request会报错,故在这里使用
|
||
if (!props.web && props.src.startsWith('http')) {
|
||
if(map.has(props.src)) {
|
||
nativeImage?.updateSrc(map.get(props.src)!)
|
||
return
|
||
}
|
||
uni.downloadFile({
|
||
url: props.src,
|
||
success(res) {
|
||
path.value = res.tempFilePath
|
||
map.set(props.src, res.tempFilePath)
|
||
nativeImage?.updateSrc(res.tempFilePath)
|
||
}
|
||
})
|
||
} else {
|
||
// const a = UTSAndroid.convert2AbsFullPath(props.src)
|
||
path.value = props.src;
|
||
nativeImage?.updateSrc(props.src)
|
||
}
|
||
// #endif
|
||
// #ifdef APP-HARMONY
|
||
path.value = props.src;
|
||
nativeImage?.updateSrc(props.src)
|
||
// #endif
|
||
})
|
||
watchEffect(() => {
|
||
if(props.color == '') return
|
||
nativeImage?.updateColor(props.color)
|
||
})
|
||
// #endif
|
||
|
||
|
||
|
||
// 小程序
|
||
// #ifndef APP || WEB
|
||
watchEffect(() => {
|
||
if (props.src == '') return
|
||
if (props.src.startsWith('<svg')) {
|
||
path.value = svgToDataUrl(props.src)
|
||
} else if (props.src.startsWith('/static')) {
|
||
pathToDataUrl(props.src).then(res => {
|
||
path.value = res;
|
||
}).catch(err => {
|
||
emit('error', errorEvent)
|
||
console.warn("[lime-svg]" + props.src + JSON.stringify(err))
|
||
})
|
||
} else {
|
||
path.value = props.src
|
||
}
|
||
})
|
||
// #endif
|
||
</script>
|
||
|
||
<style lang="scss">
|
||
.l-svg {
|
||
// align-self: flex-start;
|
||
/* #ifdef APP */
|
||
width: 24px;
|
||
height: 24px;
|
||
/* #endif */
|
||
/* #ifndef APP */
|
||
width: 1em;
|
||
height: 1em;
|
||
/* #endif */
|
||
/* #ifndef APP */
|
||
:deep(svg) {
|
||
width: 100%;
|
||
height: 100%;
|
||
}
|
||
|
||
&-img {
|
||
mix-blend-mode: lighten;
|
||
width: 100%;
|
||
height: 100%;
|
||
}
|
||
|
||
&.is-inherit {
|
||
-webkit-mask-image: var(--svg);
|
||
mask-image: var(--svg);
|
||
-webkit-mask-repeat: no-repeat;
|
||
mask-repeat: no-repeat;
|
||
-webkit-mask-size: 100% 100%;
|
||
mask-size: 100% 100%;
|
||
background-color: currentColor;
|
||
}
|
||
|
||
&:not(.is-inherit) {
|
||
background: var(--svg) no-repeat;
|
||
background-size: 100% 100%;
|
||
background-color: transparent;
|
||
|
||
image {
|
||
mix-blend-mode: inherit;
|
||
opacity: 0;
|
||
}
|
||
}
|
||
|
||
/* #endif */
|
||
}
|
||
</style> |