合同小程序前端代码仓库
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

269 lines
6.6 KiB

3 months ago
  1. <template>
  2. <!-- #ifdef APP -->
  3. <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>
  4. <l-svg-x class="l-svg" v-else :src="path" :color="color" @error="onError" @load="onLoad" @click="$emit('click')"></l-svg-x>
  5. <!-- #endif -->
  6. <!-- #ifdef WEB -->
  7. <view class="l-svg" v-if="src.startsWith('<svg')" v-html="src" :style="styles" @click="$emit('click')"></view>
  8. <view class="l-svg" :class="{'is-inherit': isInherit}" v-else :style="styles" @click="$emit('click')">
  9. <image class="l-svg-img" :src="src" @error="onError" @load="onLoad"/>
  10. </view>
  11. <!-- #endif -->
  12. <!-- #ifndef APP || WEB -->
  13. <view class="l-svg" :class="{'is-inherit': isInherit}" :style="styles" @click="$emit('click')">
  14. <image class="l-svg-img" :src="path" @error="onError" @load="onLoad"></image>
  15. </view>
  16. <!-- #endif -->
  17. </template>
  18. <script setup lang="uts">
  19. import { LSvpProps } from './type'
  20. // #ifndef APP || WEB
  21. import { pathToDataUrl, svgToDataUrl } from './utils'
  22. // #endif
  23. // #ifdef APP
  24. import { pathToDataUrl, svgToDataUrl } from './utils'
  25. // #endif
  26. const props = withDefaults(defineProps<LSvpProps>(), {
  27. src: '',
  28. color: '',
  29. web: false,
  30. inherit: false
  31. })
  32. const emits = defineEmits(['load', 'error', 'click'])
  33. const path = ref(props.src)
  34. // #ifndef APP
  35. const isInherit = computed(():boolean => {
  36. return props.color != ''
  37. })
  38. // #endif
  39. const imageURL = ref('')
  40. const formatUrl = (url: string, action:string):string => {
  41. if(url.indexOf(`'`) > 0) return `${action}("${url}")`
  42. return `${action}('${url}')`
  43. }
  44. const styles = computed(() : Map<string, string> => {
  45. const style = new Map<string, string>()
  46. // #ifdef WEB
  47. if (props.src != '' && !props.src.startsWith('<svg')) {
  48. // style.set('--svg', formatUrl(props.src, 'url'))
  49. style.set('--svg', formatUrl(imageURL.value ?? props.src, 'url'))
  50. }
  51. // #endif
  52. // #ifndef APP || WEB
  53. if (path.value != '') {
  54. // style.set('--svg', formatUrl(props.src, 'url'))
  55. style.set('--svg', formatUrl(path.value, 'url'))
  56. }
  57. // #endif
  58. if (props.color != '') {
  59. style.set('color', props.color)
  60. }
  61. return style
  62. })
  63. // #ifdef APP-ANDROID
  64. const errorDetaill = new UniImageErrorEventDetail('加载失败')
  65. const errorEvent = new UniImageErrorEvent('error', errorDetaill)
  66. // #endif
  67. // #ifndef APP-ANDROID
  68. const errorDetaill = {
  69. errMsg: '加载失败'
  70. }
  71. const errorEvent = {
  72. type: 'error',
  73. detaill: errorDetaill
  74. }
  75. // #endif
  76. const onError = () => {
  77. emits('error', errorEvent)
  78. }
  79. const onLoad = (e: UniImageLoadEvent) => {
  80. // #ifdef WEB
  81. // @ts-ignore
  82. imageURL.value = e.target.src
  83. // #endif
  84. // #ifdef APP-ANDROID
  85. // const detail = new ImageLoadEventDetail(512, 512)
  86. const loadEvent = e;//new UniImageLoadEvent('load', detail)
  87. // #endif
  88. // #ifndef APP-ANDROID
  89. const detail = {
  90. width: 512,
  91. height:512
  92. }
  93. const loadEvent = {
  94. type: 'error',
  95. detail
  96. }
  97. // #endif
  98. emits('load', loadEvent)
  99. }
  100. // app
  101. // #ifdef APP
  102. const webRef = ref<UniWebViewElement|null>(null)
  103. const setSvgSrc = () => {
  104. if(path.value != '') {
  105. webRef.value?.evalJS(formatUrl(path.value, 'setSrc'));
  106. }
  107. }
  108. const setSvgColor = () => {
  109. if(props.color != '' && path.value != '') {
  110. webRef.value?.evalJS(`setStyle({"--color": "${props.color}"})`);
  111. }
  112. }
  113. const error = (_: UniWebViewErrorEvent) => {
  114. emits('error', errorEvent)
  115. }
  116. const loaded = (_: UniWebViewLoadEvent) => {
  117. watchEffect(() =>{
  118. if (props.src == '' || !props.web) return
  119. if (props.src.startsWith('<svg')) {
  120. path.value = svgToDataUrl(props.src)
  121. setSvgSrc()
  122. setSvgColor()
  123. } else if(props.src.startsWith('/static')) {
  124. pathToDataUrl(props.src).then(res => {
  125. path.value = res;
  126. setSvgSrc()
  127. setSvgColor()
  128. }).catch(err => {
  129. emits('error', errorEvent)
  130. console.warn("[lime-svg]" + props.src +JSON.stringify(err))
  131. })
  132. } else {
  133. path.value = props.src
  134. setSvgSrc()
  135. setSvgColor()
  136. }
  137. })
  138. }
  139. const message = (event: UniWebViewMessageEvent) => {
  140. const data = UTSJSONObject.assign({}, event.detail.data[0] as UTSJSONObject); //event.detail.data[0] as UTSJSONObject
  141. const type = data.getString('event')
  142. // #ifdef APP-ANDROID
  143. const detail = data.getJSON('data')?.getJSON('detail')
  144. // #endif
  145. // #ifndef APP-ANDROID
  146. const detail = UTSJSONObject.assign({}, data?.data?.detail ?? {})
  147. // #endif
  148. if(type == 'click') {
  149. emits('click')
  150. }else if(type == 'load') {
  151. const width = detail?.getNumber('width') ?? 512
  152. const height = detail?.getNumber('height') ?? 512
  153. // #ifdef APP-ANDROID
  154. const loadDetail = new ImageLoadEventDetail(width, height)
  155. const loadEvent = new UniImageLoadEvent('load', loadDetail)
  156. // #endif
  157. // #ifndef APP-ANDROID
  158. const loadDetail = {
  159. width,
  160. height
  161. }
  162. const loadEvent = {
  163. type: 'error',
  164. detail:loadDetail
  165. }
  166. // #endif
  167. emits(type, loadEvent)
  168. } else if(type == 'error') {
  169. emits(type, errorEvent)
  170. }
  171. }
  172. // #endif
  173. // #ifdef APP
  174. // ios uts组件使用uni.request会报错,故在这里使用
  175. watchEffect(()=>{
  176. if(!props.web && props.src.startsWith('http')) {
  177. uni.downloadFile({
  178. url: props.src,
  179. success(res) {
  180. path.value = res.tempFilePath
  181. }
  182. })
  183. // uni.request({
  184. // url: props.src,
  185. // dataType: 'text',
  186. // success(res) {
  187. // path.value = res.data as string as string | null ?? ''
  188. // }
  189. // })
  190. } else {
  191. path.value = props.src;
  192. }
  193. })
  194. // #endif
  195. // 小程序
  196. // #ifndef APP || WEB
  197. watchEffect(() => {
  198. if (props.src == '') return
  199. if (props.src.startsWith('<svg')) {
  200. path.value = svgToDataUrl(props.src)
  201. } else if (props.src.startsWith('/static')) {
  202. pathToDataUrl(props.src).then(res => {
  203. path.value = res;
  204. }).catch(err => {
  205. emits('error', errorEvent)
  206. console.warn("[lime-svg]" + props.src + JSON.stringify(err))
  207. })
  208. } else {
  209. path.value = props.src
  210. }
  211. })
  212. // #endif
  213. </script>
  214. <style lang="scss">
  215. .l-svg {
  216. // align-self: flex-start;
  217. /* #ifdef APP */
  218. width: 24px;
  219. height: 24px;
  220. /* #endif */
  221. /* #ifndef APP */
  222. width: 1em;
  223. height: 1em;
  224. /* #endif */
  225. /* #ifndef APP */
  226. :deep(svg) {
  227. width: 100%;
  228. height: 100%;
  229. }
  230. &-img {
  231. mix-blend-mode: lighten;
  232. width: 100%;
  233. height: 100%;
  234. }
  235. &.is-inherit {
  236. -webkit-mask-image: var(--svg);
  237. mask-image: var(--svg);
  238. -webkit-mask-repeat: no-repeat;
  239. mask-repeat: no-repeat;
  240. -webkit-mask-size: 100% 100%;
  241. mask-size: 100% 100%;
  242. background-color: currentColor;
  243. }
  244. &:not(.is-inherit) {
  245. background: var(--svg) no-repeat;
  246. background-size: 100% 100%;
  247. background-color: transparent;
  248. image {
  249. mix-blend-mode: inherit;
  250. opacity: 0;
  251. }
  252. }
  253. /* #endif */
  254. }
  255. </style>