l-echart.uvue 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373
  1. <template>
  2. <!-- #ifdef APP -->
  3. <web-view class="lime-echart" ref="chartRef" @load="loaded" :style="[customStyle]" :webview-styles="[webviewStyles]"
  4. src="/uni_modules/lime-echart/static/uvue.html?v=10112">
  5. </web-view>
  6. <!-- #endif -->
  7. <!-- #ifdef H5 -->
  8. <div class="lime-echart" ref="chartRef"></div>
  9. <!-- #endif -->
  10. <!-- #ifndef H5 || APP-->
  11. <view class="lime-echart">
  12. <canvas style="width:100%; height:100%" v-if="canvasid" :id="canvasid" @touchstart="touchstart" @touchmove="touchmove" @touchend="touchend"></canvas>
  13. </view>
  14. <!-- #endif -->
  15. </template>
  16. <script lang="uts" setup>
  17. // @ts-nocheck
  18. import { getCurrentInstance, nextTick } from "vue";
  19. import { Echarts } from './uvue';
  20. // #ifdef WEB
  21. import { dispatch } from './canvas';
  22. // #endif
  23. // #ifndef APP || WEB
  24. import { Canvas, setCanvasCreator, dispatch } from './canvas';
  25. import { wrapTouch, convertTouchesToArray, devicePixelRatio, sleep, canIUseCanvas2d, getRect } from './utils';
  26. // #endif
  27. type EchartsResolve = (value : Echarts) => void
  28. defineOptions({
  29. name: 'l-echart'
  30. })
  31. const emits = defineEmits(['finished'])
  32. const props = defineProps({
  33. // #ifdef APP
  34. webviewStyles: {
  35. type: Object
  36. },
  37. customStyle: {
  38. type: Object
  39. },
  40. // #endif
  41. // #ifndef APP
  42. webviewStyles: {
  43. type: Object
  44. },
  45. customStyle: {
  46. type: [String, Object]
  47. },
  48. // #endif
  49. isDisableScroll: {
  50. type: Boolean,
  51. default: false
  52. },
  53. isClickable: {
  54. type: Boolean,
  55. default: true
  56. },
  57. enableHover: {
  58. type: Boolean,
  59. default: false
  60. },
  61. beforeDelay: {
  62. type: Number,
  63. default: 30
  64. }
  65. })
  66. const instance = getCurrentInstance()!;
  67. const canvasid = `lime-echart-${instance.uid}`
  68. const finished = ref(false)
  69. const map = [] as EchartsResolve[]
  70. const callbackMap = [] as EchartsResolve[]
  71. // let context = null as UniWebViewElement | null
  72. let chart = null as Echarts | null
  73. let chartRef = ref<UniWebViewElement | null>(null)
  74. const trigger = () => {
  75. // #ifdef APP
  76. if (finished.value) {
  77. if (chart == null) {
  78. chart = new Echarts(chartRef.value!)
  79. }
  80. while (map.length > 0) {
  81. const resolve = map.pop() as EchartsResolve
  82. resolve(chart!)
  83. }
  84. }
  85. // #endif
  86. // #ifndef APP
  87. while (map.length > 0) {
  88. if (chart != null) {
  89. const resolve = map.pop() as EchartsResolve
  90. resolve(chart!)
  91. }
  92. }
  93. // #endif
  94. if (chart != null) {
  95. while (callbackMap.length > 0) {
  96. const callback = callbackMap.pop() as EchartsResolve
  97. callback(chart!)
  98. }
  99. }
  100. }
  101. // #ifdef APP
  102. // const resizeObserver = new UniResizeObserver((entries : Array<UniResizeObserverEntry>) => {
  103. // const rect = entries[0].target.getBoundingClientRect()
  104. // if((rect.width > 0 || rect.height > 0) && !finished.value) {
  105. // finished.value = true
  106. // trigger()
  107. // emits('finished')
  108. // }
  109. // })
  110. // const stopWatch = watch(():UniElement|null => chartRef.value, (el:UniElement|null) => {
  111. // if(el== null) return
  112. // resizeObserver.observe(el)
  113. // })
  114. const loaded = (event : UniWebViewLoadEvent) => {
  115. event.stopPropagation()
  116. event.preventDefault()
  117. nextTick(()=> {
  118. chartRef.value?.getBoundingClientRectAsync()?.then(res => {
  119. if(res.width > 0 && res.height > 0) {
  120. finished.value = true
  121. trigger()
  122. emits('finished')
  123. } else {
  124. console.warn('【lime-echart】获取尺寸失败,请检查代码样式')
  125. }
  126. })
  127. })
  128. }
  129. // #endif
  130. const _next = () : boolean => {
  131. if (chart == null) {
  132. console.warn(`组件还未初始化,请先使用 init`)
  133. return true
  134. }
  135. return false
  136. }
  137. const setOption = (option : UTSJSONObject) => {
  138. if (_next()) return
  139. chart!.setOption(option);
  140. }
  141. const showLoading = () => {
  142. if (_next()) return
  143. chart!.showLoading();
  144. }
  145. const hideLoading = () => {
  146. if (_next()) return
  147. chart!.hideLoading();
  148. }
  149. const clear = () => {
  150. if (_next()) return
  151. chart!.clear();
  152. }
  153. const dispose = () => {
  154. if (_next()) return
  155. chart!.dispose();
  156. }
  157. const resize = (size : UTSJSONObject) => {
  158. if (_next()) return
  159. chart!.resize(size);
  160. }
  161. const canvasToTempFilePath = (opt : UTSJSONObject) => {
  162. if (_next()) return
  163. chart!.canvasToTempFilePath(opt);
  164. }
  165. // #ifdef APP
  166. function init(callback : ((chart : Echarts) => void) | null) : Promise<Echarts> {
  167. if (callback != null) {
  168. callbackMap.push(callback)
  169. }
  170. return new Promise<Echarts>((resolve) => {
  171. map.push(resolve)
  172. trigger()
  173. })
  174. }
  175. // onUnmounted(()=>{
  176. // stopWatch()
  177. // resizeObserver.disconnect()
  178. // })
  179. // #endif
  180. // #ifndef APP
  181. // #ifndef WEB
  182. let use2dCanvas = canIUseCanvas2d()
  183. const getContext = async () => {
  184. return new Promise((resolve, reject)=>{
  185. uni.createCanvasContextAsync({
  186. id: canvasid,
  187. component: instance.proxy!,
  188. success: (context : CanvasContext) => {
  189. const canvasContext = context.getContext('2d')!;
  190. const canvas = canvasContext.canvas;
  191. let uniCanvas;
  192. const width = canvas.offsetWidth
  193. const height = canvas.offsetHeight
  194. // 处理高清屏逻辑
  195. const dpr = uni.getDeviceInfo().devicePixelRatio ?? 1;
  196. canvas.width = canvas.offsetWidth * dpr;
  197. canvas.height = canvas.offsetHeight * dpr;
  198. canvasContext.scale(dpr, dpr); // 仅需调用一次,当调用 reset 方法后需要再次 scale
  199. if(use2dCanvas) {
  200. uniCanvas = new Canvas(canvasContext, instance.proxy, true, context);
  201. } else {
  202. uniCanvas = new Canvas(canvasContext, instance.proxy, false);
  203. }
  204. resolve({ canvas: uniCanvas, width, height, devicePixelRatio: 1, node: context});
  205. },
  206. fail(err) {
  207. reject(err)
  208. console.log('err', err)
  209. }
  210. })
  211. })
  212. // return getRect(`#${canvasid}`, {context: instance.proxy!, type: use2dCanvas ? 'fields': 'boundingClientRect'}).then(res => {
  213. // if(res) {
  214. // let dpr = uni.getWindowInfo().pixelRatio
  215. // let {width, height, node} = res
  216. // let canvas;
  217. // if(node) {
  218. // const ctx = node.getContext('2d');
  219. // canvas = new Canvas(ctx, instance.proxy, true, node);
  220. // } else {
  221. // const ctx = uni.createCanvasContext(canvasid, instance.proxy);
  222. // canvas = new Canvas(ctx, instance.proxy, false);
  223. // }
  224. // return { canvas, width, height, devicePixelRatio: dpr, node };
  225. // } else {
  226. // return {}
  227. // }
  228. // })
  229. }
  230. // #endif
  231. const getTouch = (e) => {
  232. const touches = e.touches[0]
  233. // #ifdef WEB
  234. const rect = chart!.getZr().dom.getBoundingClientRect();
  235. const touch = {
  236. x: touches.clientX - rect.left,
  237. y: touches.clientY - rect.top
  238. }
  239. // #endif
  240. // #ifndef WEB
  241. const touch = {
  242. x: touches.x,
  243. y: touches.y
  244. }
  245. // #endif
  246. return touch
  247. }
  248. const touchstart = (e) => {
  249. if (chart == null) return
  250. const handler = chart.getZr().handler;
  251. const touch = getTouch(e)
  252. dispatch.call(handler, 'mousedown', touch)
  253. dispatch.call(handler, 'click', touch)
  254. }
  255. const touchmove = (e) => {
  256. if (chart == null) return
  257. const handler = chart.getZr().handler;
  258. const touch = getTouch(e)
  259. dispatch.call(handler, 'mousemove', touch)
  260. // const rect = chart.getZr().dom.getBoundingClientRect()
  261. // handler.dispatch('mousemove', {
  262. // zrX: e.touches[0].clientX - rect.left,
  263. // zrY: e.touches[0].clientY - rect.top
  264. // })
  265. }
  266. const touchend = (e) => {
  267. if (chart == null) return
  268. const handler = chart.getZr().handler;
  269. const touch = {
  270. x: 999999999,
  271. y: 999999999
  272. }
  273. dispatch.call(handler, 'mousemove', touch)
  274. dispatch.call(handler, 'touchend', touch)
  275. }
  276. async function init(echarts : any, ...args : any[]) : Promise<Echarts> {
  277. if (echarts == null) {
  278. console.error('请确保已经引入了 ECharts 库');
  279. return Promise.reject('请确保已经引入了 ECharts 库');
  280. }
  281. let theme : string | null = null
  282. let opts = {}
  283. let callback : Function | null = null;
  284. args.forEach(item => {
  285. if (typeof item === 'function') {
  286. callback = item
  287. } else if (['string'].includes(typeof item)) {
  288. theme = item
  289. } else if (typeof item === 'object') {
  290. opts = item
  291. }
  292. })
  293. // #ifdef WEB
  294. echarts.env.domSupported = true
  295. echarts.env.hasGlobalWindow = true
  296. echarts.env.node = false
  297. echarts.env.pointerEventsSupported = false
  298. echarts.env.svgSupported = true
  299. echarts.env.touchEventsSupported = true
  300. echarts.env.transform3dSupported = true
  301. echarts.env.transformSupported = true
  302. echarts.env.worker = false
  303. echarts.env.wxa = false
  304. chart = echarts.init(chartRef.value, theme, opts)
  305. // window.addEventListener('touchstart', touchstart)
  306. // window.addEventListener('touchmove', touchmove)
  307. // window.addEventListener('touchend', touchend)
  308. // #endif
  309. // #ifndef WEB
  310. let config = await getContext();
  311. setCanvasCreator(echarts, config)
  312. chart = echarts.init(config.canvas, theme, Object.assign({}, config, opts))
  313. // #endif
  314. if (callback != null && typeof callback == 'function') {
  315. callbackMap.push(callback)
  316. }
  317. return new Promise<Echarts>((resolve) => {
  318. map.push(resolve)
  319. trigger()
  320. })
  321. }
  322. onMounted(() => {
  323. nextTick(() => {
  324. finished.value = true
  325. trigger()
  326. emits('finished')
  327. })
  328. })
  329. onUnmounted(() => {
  330. // #ifdef WEB
  331. // window.removeEventListener('touchstart', touchstart)
  332. // window.removeEventListener('touchmove', touchmove)
  333. // window.removeEventListener('touchend', touchend)
  334. // #endif
  335. })
  336. // #endif
  337. defineExpose({
  338. init,
  339. setOption,
  340. showLoading,
  341. hideLoading,
  342. clear,
  343. dispose,
  344. resize,
  345. canvasToTempFilePath
  346. })
  347. </script>
  348. <style lang="scss">
  349. .lime-echart {
  350. flex: 1;
  351. width: 100%;
  352. }
  353. </style>