canvas.uvue 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258
  1. <template>
  2. <view class="page" id="page-canvas">
  3. <canvas id="canvas" class="canvas-element"></canvas>
  4. <scroll-view class="scroll-view">
  5. <!-- #ifdef WEB -->
  6. <button class="canvas-drawing-button" @click="canvasToBlob">canvasToBlob</button>
  7. <view>
  8. <text>testToBlobResult: {{testToBlobResult}}</text>
  9. </view>
  10. <!-- #endif -->
  11. <button class="canvas-drawing-button" id="toDataURL" @click="canvasToDataURL">canvasToDataURL</button>
  12. <view class="text-group" v-if="dataBase64.length>0">
  13. <text>canvasToDataURL:</text>
  14. <text>{{dataBase64.slice(0,22)}}...</text>
  15. </view>
  16. <button @click="onCreateImage">createImage</button>
  17. <button @click="onCreatePath2D">createPath2D</button>
  18. <button @click="startAnimationFrame">requestAnimationFrame</button>
  19. <button @click="stopAnimationFrame">cancelAnimationFrame</button>
  20. <view style="padding: 8px 10px;">CanvasContext API 演示</view>
  21. <navigator url="./canvas-context">
  22. <button>CanvasContext API</button>
  23. </navigator>
  24. <view class="text-group">
  25. <text>获取 CanvasContext 结果:</text>
  26. <text id="testCanvasContext">{{testCanvasContext}}</text>
  27. </view>
  28. <view class="text-group">
  29. <text>测试 ToDataURL 结果:</text>
  30. <text id="testToDataURLResult">{{testToDataURLResult}}</text>
  31. </view>
  32. <view class="text-group">
  33. <text>测试 createImage 结果:</text>
  34. <text id="testCreateImage">{{testCreateImage}}</text>
  35. </view>
  36. <view class="text-group">
  37. <text>测试 createPath2D 结果:</text>
  38. <text id="testCreatePath2D">{{testCreatePath2D}}</text>
  39. </view>
  40. <view class="text-group">
  41. <text>测试 createCanvasContextAsync 结果:</text>
  42. <view @click="testCreateContextAsync" id="createCanvasContextAsync">{{testCanvasCtx}}</view>
  43. </view>
  44. <canvas-child ref="canvas-child"></canvas-child>
  45. </scroll-view>
  46. </view>
  47. </template>
  48. <script>
  49. import CanvasChild from './canvas-child.uvue'
  50. function hidpi(canvas : UniCanvasElement) {
  51. const context = canvas.getContext("2d")!;
  52. const dpr = uni.getWindowInfo().pixelRatio;
  53. canvas.width = canvas.offsetWidth * dpr;
  54. canvas.height = canvas.offsetHeight * dpr;
  55. context.scale(dpr, dpr);
  56. }
  57. export default {
  58. components: {
  59. CanvasChild
  60. },
  61. data() {
  62. return {
  63. title: 'Context2D',
  64. canvas: null as UniCanvasElement | null,
  65. canvasContext: null as CanvasContext | null,
  66. renderingContext: null as CanvasRenderingContext2D | null,
  67. canvasWidth: 0,
  68. canvasHeight: 0,
  69. dataBase64: '',
  70. taskId: 0,
  71. lastTime: 0,
  72. frameCount: 0,
  73. // 仅测试
  74. testCanvasContext: false,
  75. testToBlobResult: false,
  76. testToDataURLResult: false,
  77. testCreateImage: false,
  78. testCreatePath2D: false,
  79. testFrameCount: 0,
  80. testCanvasCtx1: false,
  81. testCanvasCtx2: false,
  82. testCounter: 0
  83. }
  84. },
  85. computed: {
  86. testCanvasCtx() {
  87. return this.testCanvasCtx1 && this.testCanvasCtx2
  88. }
  89. },
  90. onLoad() {
  91. // HBuilderX 4.25+
  92. // 异步调用方式, 跨平台写法
  93. uni.createCanvasContextAsync({
  94. id: 'canvas',
  95. component: this,
  96. success: (context : CanvasContext) => {
  97. this.canvasContext = context;
  98. this.renderingContext = context.getContext('2d')!;
  99. this.canvas = this.renderingContext!.canvas;
  100. hidpi(this.canvas!);
  101. this.canvasWidth = this.canvas!.width;
  102. this.canvasHeight = this.canvas!.height;
  103. // #ifdef WEB
  104. context.toBlob((blob : Blob) => {
  105. this.testToBlobResult = (blob.size > 0 && blob.type == 'image/jpeg')
  106. }, 'image/jpeg', 0.95);
  107. // #endif
  108. // #ifdef APP || WEB || MP
  109. setTimeout(() => {
  110. this.testToDataURLResult = this.canvasContext!.toDataURL().startsWith('data:image/png;base64')
  111. }, 50)
  112. // #endif
  113. this.testCanvasContext = true
  114. }
  115. })
  116. uni.$on('canvasChildReady', this.onChildReady)
  117. },
  118. onReady() {
  119. // 同步调用方式,仅支持 app/web
  120. // let canvas = uni.getElementById("canvas") as UniCanvasElement
  121. // this.renderingContext = canvas.getContext("2d")
  122. // hidpi(canvas);
  123. // this.canvas = canvas;
  124. // this.canvasWidth = canvas.width;
  125. // this.canvasHeight = canvas.height;
  126. },
  127. onUnload() {
  128. uni.$off('canvasChildReady', this.onChildReady)
  129. if (this.taskId > 0) {
  130. this.stopAnimationFrame()
  131. }
  132. },
  133. methods: {
  134. // #ifdef WEB
  135. canvasToBlob() {
  136. this.canvasContext!.toBlob((blob : Blob) => {
  137. this.testToBlobResult = (blob.size > 0 && blob.type == 'image/jpeg')
  138. }, 'image/jpeg', 0.95)
  139. },
  140. // #endif
  141. canvasToDataURL() {
  142. this.dataBase64 = this.canvasContext!.toDataURL()
  143. },
  144. onCreateImage() {
  145. this.renderingContext!.clearRect(0, 0, this.canvasWidth, this.canvasHeight)
  146. let image = this.canvasContext!.createImage();
  147. image.src = "/static/logo.png"
  148. image.onload = () => {
  149. this.testCreateImage = true
  150. this.renderingContext?.drawImage(image, 0, 0, 100, 100);
  151. }
  152. },
  153. onCreatePath2D() {
  154. this.renderingContext!.clearRect(0, 0, this.canvasWidth, this.canvasHeight)
  155. const context = this.renderingContext!
  156. let path2D = this.canvasContext!.createPath2D()
  157. this.testCreatePath2D = true
  158. const amplitude = 64;
  159. const wavelength = 64;
  160. for (let i = 0; i < 5; i++) {
  161. const x1 = 0 + (i * wavelength);
  162. const y1 = 128;
  163. const x2 = x1 + wavelength / 4;
  164. const y2 = y1 - amplitude;
  165. const x3 = x1 + 3 * wavelength / 4;
  166. const y3 = y1 + amplitude;
  167. const x4 = x1 + wavelength;
  168. const y4 = y1;
  169. context.moveTo(x1, y1);
  170. path2D.bezierCurveTo(x2, y2, x3, y3, x4, y4);
  171. }
  172. context.stroke(path2D);
  173. },
  174. startAnimationFrame() {
  175. this.taskId = this.canvasContext!.requestAnimationFrame((timestamp : number) => {
  176. this.testFrameCount++
  177. this.updateFPS(timestamp)
  178. this.startAnimationFrame()
  179. })
  180. },
  181. stopAnimationFrame() {
  182. this.canvasContext!.cancelAnimationFrame(this.taskId)
  183. this.taskId = 0
  184. },
  185. updateFPS(timestamp : number) {
  186. this.frameCount++
  187. if (timestamp - this.lastTime >= 1000) {
  188. const timeOfFrame = (1000 / this.frameCount)
  189. this.renderingContext!.clearRect(0, 0, this.canvasWidth, this.canvasHeight)
  190. this.renderingContext!.fillText(`${this.frameCount} / ${timeOfFrame.toFixed(3)}ms`, 10, 18)
  191. this.frameCount = 0
  192. this.lastTime = timestamp
  193. }
  194. },
  195. testCreateContextAsync() {
  196. uni.createCanvasContextAsync({
  197. id: 'canvas',
  198. component: this,
  199. success: () => {
  200. this.testCanvasCtx1 = true
  201. }
  202. })
  203. // no `component` param
  204. uni.createCanvasContextAsync({
  205. id: 'canvas',
  206. success: () => {
  207. this.testCanvasCtx2 = true
  208. }
  209. })
  210. },
  211. onChildReady() {
  212. const childInstance = (this.$refs['canvas-child'] as ComponentPublicInstance);
  213. this.testCounter = childInstance.$data['testCounter'] as number;
  214. }
  215. }
  216. }
  217. </script>
  218. <style>
  219. .page {
  220. flex: 1;
  221. height: 100%;
  222. overflow: hidden;
  223. }
  224. .scroll-view {
  225. flex: 1;
  226. }
  227. .canvas-element {
  228. width: 100%;
  229. height: 250px;
  230. background-color: #ffffff;
  231. }
  232. .btn-to-image {
  233. margin: 10px;
  234. }
  235. .text-group {
  236. display: flex;
  237. flex-flow: row nowrap;
  238. justify-content: space-between;
  239. align-items: center;
  240. padding: 8px 10px;
  241. }
  242. </style>