c-lottie.vue 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272
  1. <template>
  2. <!-- #ifdef H5||APP-PLUS -->
  3. <view class="c-lottie" :style="{width,height}" :lottieData="lottieData" :change:lottieData="lottie.render" :fun='fun'
  4. :change:fun='lottie.callPlayer'>
  5. <div :id='myCanvasId'></div>
  6. </view>
  7. <!-- #endif -->
  8. <!-- #ifdef MP -->
  9. <view class="c-lottie" :style="{width,height}">
  10. <canvas class="canvas" :id="myCanvasId" type="2d"></canvas>
  11. </view>
  12. <!-- #endif -->
  13. </template>
  14. <script>
  15. /**
  16. * c-lottie Lottie组件
  17. * @property {String} canvasId 画布id
  18. * @property {String} width 图像宽度 默认750rpx 单位rpx/px
  19. * @property {String} height 图像高度 默认750rpx 单位rpx/px
  20. * @property {String} src Lottie文件地址 小程序只支持网络地址
  21. * @property {String} data Lottie文件data
  22. * @property {Boolean} autoPlay 是否自动播放 默认true
  23. * @property {Boolean} loop 是否循环播放 默认值为 true
  24. * @property {String} renderer 渲染模式 默认值为canvas 可选值 svg|canvas 小程序不支持
  25. * @property {Boolean} isOnChange 是否开启播放进度监听 默认false false时不触发EnterFrame
  26. * @event {Function()} Complete 监听动画播放完成
  27. * @event {Function()} LoopComplete 监听当前循环播放完成
  28. * @event {Function()} EnterFrame 监听动画播放进度
  29. * @event {Function()} SegmentStart 监听开始播放一个动画片段
  30. * @event {Function()} dataReady 当动画的所有部分都已加载时触发
  31. * @event {Function()} dataFailed 当部分动画无法加载时触发
  32. * 组件内方法统一使用 call(funName, args) 调用player实例方法 详见文档
  33. * */
  34. import uuid from './js/uuid.js'
  35. // #ifdef MP&VUE3
  36. import lottie from 'lottie-miniprogram'
  37. // #endif
  38. // #ifdef MP&VUE2
  39. import lottie from '../../node_modules/lottie-miniprogram'
  40. // #endif
  41. // #ifdef MP
  42. let player, context, myCanvas;
  43. // #endif
  44. export default {
  45. props: {
  46. canvasId: {
  47. type: String
  48. },
  49. width: {
  50. type: String,
  51. default: '750rpx'
  52. },
  53. height: {
  54. type: String,
  55. default: '750rpx'
  56. },
  57. src: {
  58. type: String,
  59. },
  60. data: {
  61. type: String,
  62. },
  63. autoPlay: { //是否自动播放
  64. type: Boolean,
  65. default: true
  66. },
  67. loop: { //是否循环播放 默认值为 true
  68. type: Boolean,
  69. default: true
  70. },
  71. renderer: { //渲染模式 默认值为canvas 可选值 svg
  72. type: String,
  73. default: 'canvas',
  74. },
  75. isOnChange: {
  76. type: Boolean,
  77. default: false
  78. }
  79. },
  80. emits: ['Complete', 'LoopComplete', 'EnterFrame', 'SegmentStart','dataReady','dataFailed'],
  81. data() {
  82. return {
  83. fun: {}
  84. }
  85. },
  86. computed: {
  87. myCanvasId() {
  88. if (!this.canvasId) {
  89. return 'c' + uuid(18)
  90. } else {
  91. return this.canvasId
  92. }
  93. },
  94. lottieData() {
  95. return {
  96. myCanvasId: this.myCanvasId,
  97. width: this.width,
  98. height: this.height,
  99. src: this.src,
  100. data: this.data,
  101. autoPlay: this.autoPlay,
  102. loop: this.loop,
  103. renderer: this.renderer,
  104. isOnChange: this.isOnChange
  105. }
  106. }
  107. },
  108. watch: {
  109. lottieData() {
  110. // #ifdef MP
  111. this.render()
  112. // #endif
  113. }
  114. },
  115. methods: {
  116. call(name, args) {
  117. this.fun = {name,args}
  118. // #ifdef MP
  119. this.callPlayer(this.fun)
  120. // #endif
  121. },
  122. // #ifdef MP
  123. getContext() {
  124. return new Promise((resolve) => {
  125. const {
  126. pixelRatio
  127. } = uni.getSystemInfoSync()
  128. uni.createSelectorQuery()
  129. .in(this)
  130. .select(`#${this.myCanvasId}`)
  131. .fields({
  132. node: true,
  133. size: true
  134. })
  135. .exec(res => {
  136. const {
  137. width,
  138. height
  139. } = res[0]
  140. const canvas = res[0].node
  141. resolve({
  142. canvas,
  143. width,
  144. height,
  145. pixelRatio
  146. })
  147. })
  148. })
  149. },
  150. async render() {
  151. if (player) {
  152. // console.log(player);
  153. // call('stop')
  154. this.call('destroy', player)
  155. }
  156. let {
  157. canvas,
  158. width,
  159. height,
  160. pixelRatio
  161. } = await this.getContext()
  162. myCanvas = canvas
  163. context = canvas.getContext('2d')
  164. //避免出现锯齿问题
  165. context.scale(pixelRatio, pixelRatio)
  166. canvas.width = width * pixelRatio
  167. canvas.height = height * pixelRatio
  168. lottie.setup(myCanvas)
  169. player = lottie.loadAnimation({
  170. loop: this.loop,
  171. autoplay: this.autoPlay,
  172. // 动画json的本地数据
  173. animationData: this.data,
  174. //远程动画。一定要把json格式的文件放到服务器中,并且注意域名是合法的
  175. path: this.src,
  176. rendererSettings: {
  177. context,
  178. },
  179. })
  180. player.addEventListener('data_ready',(val)=>{ //当动画的所有部分都已加载时
  181. this.$emit('dataReady', val)
  182. })
  183. player.addEventListener('data_failed',(val)=>{ //当部分动画无法加载时
  184. this.$emit('dataFailed', val)
  185. })
  186. player.onComplete = (val) => { //动画播放完成触发
  187. // console.log('动画播放完成触发',val);
  188. this.$emit('Complete', val)
  189. }
  190. player.onLoopComplete = (val) => { //当前循环播放完成触发
  191. // console.log('当前循环播放完成触发',val);
  192. this.$emit('LoopComplete', val)
  193. }
  194. if (this.isOnChange) {
  195. player.onEnterFrame = (val) => { //播放动画的时候触发
  196. // console.log('播放动画的时候触发',val);
  197. this.$emit('EnterFrame', val)
  198. }
  199. }
  200. player.onSegmentStart = (val) => { //开始播放一个动画片段的时候触发
  201. // console.log('开始播放一个动画片段的时候触发',val);
  202. this.$emit('SegmentStart', val)
  203. }
  204. },
  205. callPlayer(val) {
  206. if (!val.name) return;
  207. let {name,args} = val
  208. // console.log(name, args);
  209. if (Array.isArray(args)) {
  210. player[name](...args)
  211. } else {
  212. player[name](args)
  213. }
  214. },
  215. // #endif
  216. // #ifndef MP
  217. receiveRenderData(val) {
  218. // console.log(val);
  219. this.$emit(val.name, val.val)
  220. }
  221. // #endif
  222. },
  223. mounted() {
  224. // #ifdef MP
  225. this.render()
  226. // #endif
  227. },
  228. beforeDestroy() {
  229. // 组件卸载销毁实例
  230. this.call('destroy')
  231. }
  232. }
  233. </script>
  234. <!-- #ifndef MP -->
  235. <!-- #ifdef VUE3 -->
  236. <script lang="renderjs" src='./js/render.js' module='lottie'></script>
  237. <!-- #endif -->
  238. <!-- #ifdef VUE2 -->
  239. <script lang="renderjs" module='lottie'>
  240. import lottieRender from "./js/render.js"
  241. export default {
  242. mixins:[lottieRender],
  243. }
  244. </script>
  245. <!-- #endif -->
  246. <!-- #endif -->
  247. <style lang="scss" scoped>
  248. .c-lottie {
  249. // width: v-bind(width);
  250. // height: v-bind(height);
  251. /* #ifndef MP */
  252. div {
  253. width: 100%;
  254. height: 100%;
  255. }
  256. /* #endif */
  257. .canvas {
  258. width: 100%;
  259. height: 100%;
  260. }
  261. }
  262. </style>