Преглед изворни кода

fix(FamilyRelationGraph): 完善Canvas渲染逻辑

1. 模拟改用 setTimeout 代替 requestAnimationFrame,避免微信限制
2. stopSimulation 用 clearTimeout 替代 cancelAnimationFrame
3. simulationTick 改用 setTimeout(33ms) 轮询
4. render() 添加 Canvas 节点未就绪时的重试逻辑(最多5次)
5. renderLegacy() 完整迁移 Canvas 2D API(ctx.strokeStyle/ctx.fillStyle/
   ctx.lineWidth/ctx.font/ctx.textAlign/ctx.textBaseline 直接赋值)
6. 新增 hexToRgba 辅助函数
openhands пре 3 месеци
родитељ
комит
0ca63ac87e
1 измењених фајлова са 53 додато и 28 уклоњено
  1. 53 28
      cfc-frontend/components/FamilyRelationGraph.vue

+ 53 - 28
cfc-frontend/components/FamilyRelationGraph.vue

@@ -234,16 +234,20 @@ export default {
       this.startSimulation()
     },
 
-    startSimulation: function() {
+startSimulation: function() {
       if (this.isSimulating) return
       this.isSimulating = true
-      this.simulationTick()
+      // 延迟一帧启动,确保DOM已更新
+      var self = this
+      setTimeout(function() {
+        self.simulationTick()
+      }, 50)
     },
 
     stopSimulation: function() {
       this.isSimulating = false
       if (this.animFrameId) {
-        cancelAnimationFrame(this.animFrameId)
+        clearTimeout(this.animFrameId)
         this.animFrameId = null
       }
     },
@@ -329,10 +333,10 @@ export default {
       // 6. 渲染
       this.render()
 
-      // 7. 下一帧
-      this.animFrameId = requestAnimationFrame(function() {
+      // 7. 下一帧(使用 setTimeout 代替 requestAnimationFrame)
+      this.animFrameId = setTimeout(function() {
         self.simulationTick()
-      })
+      }, 33)
     },
 
     // ===== 渲染 =====
@@ -344,9 +348,17 @@ export default {
         .fields({ node: true, size: true })
         .exec(function(res) {
           if (!res || !res[0] || !res[0].node) {
-            self.renderLegacy()
+            // Canvas 节点尚未就绪,延迟重试
+            if (self._retryCount === undefined) self._retryCount = 0
+            if (self._retryCount < 5) {
+              self._retryCount++
+              setTimeout(function() {
+                self.render()
+              }, 200)
+            }
             return
           }
+          self._retryCount = 0
           var canvas = res[0].node
           var ctx = canvas.getContext('2d')
           canvas.width = self.canvasWidth * self.dpr
@@ -431,23 +443,36 @@ export default {
       }
     },
 
-    renderLegacy: function() {
+renderLegacy: function() {
       var ctx = uni.createCanvasContext('forceGraphCanvas', this)
-      var self = this
+      if (!ctx) return
+      
+      // hex转rgba辅助
+      function hexToRgba(hex, alpha) {
+        if (hex && hex.length === 7) {
+          var r = parseInt(hex.slice(1, 3), 16)
+          var g = parseInt(hex.slice(3, 5), 16)
+          var b = parseInt(hex.slice(5, 7), 16)
+          return 'rgba(' + r + ',' + g + ',' + b + ',' + alpha + ')'
+        }
+        return hex || '#FF6B9D'
+      }
+
       ctx.clearRect(0, 0, this.canvasWidth, this.canvasHeight)
+
       var edges = this.edges
       for (var i = 0; i < edges.length; i++) {
         var edge = edges[i]
-        var trust = edge.trust || 75
         var comm = edge.communication || 50
-        var hue = 120 * trust / 100
-        ctx.setStrokeStyle('hsla(' + hue + ', 75%, 45%, 0.6)')
-        ctx.setLineWidth(1 + (comm / 100) * 4)
+        var hue = 120 * (edge.trust || 75) / 100
+        ctx.strokeStyle = 'hsla(' + hue + ', 75%, 45%, 0.6)'
+        ctx.lineWidth = 1 + (comm / 100) * 4
         ctx.beginPath()
         ctx.moveTo(edge.source.x, edge.source.y)
         ctx.lineTo(edge.target.x, edge.target.y)
         ctx.stroke()
       }
+      
       var nodes = this.simNodes
       for (var j = 0; j < nodes.length; j++) {
         var node = nodes[j]
@@ -456,43 +481,43 @@ export default {
         if (node.isSelf) {
           ctx.beginPath()
           ctx.arc(node.x, node.y, r, 0, 2 * Math.PI)
-          ctx.setFillStyle('#FFFFFF')
+          ctx.fillStyle = '#FFFFFF'
           ctx.fill()
-          ctx.setStrokeStyle(this.themeColor)
-          ctx.setLineWidth(3)
+          ctx.strokeStyle = this.themeColor
+          ctx.lineWidth = 3
           ctx.stroke()
           // inner ring
           ctx.beginPath()
           ctx.arc(node.x, node.y, r * 0.7, 0, 2 * Math.PI)
-          ctx.setStrokeStyle(this.themeColor + '88')
-          ctx.setLineWidth(2)
+          ctx.strokeStyle = hexToRgba(this.themeColor, 0.53)
+          ctx.lineWidth = 2
           ctx.stroke()
         } else if (node.relationshipType === 'spouse') {
           this.drawHeartLegacy(ctx, node.x, node.y, r)
-          ctx.setFillStyle(this.themeColor + '22')
+          ctx.fillStyle = hexToRgba(this.themeColor, 0.13)
           ctx.fill()
         } else if (node.relationshipType === 'parent') {
           this.drawRoundedRectLegacy(ctx, node.x, node.y, r * 2, r * 2, r * 0.3)
-          ctx.setFillStyle(this.themeColor + '22')
+          ctx.fillStyle = hexToRgba(this.themeColor, 0.13)
           ctx.fill()
         } else if (node.relationshipType === 'sibling') {
           this.drawPolygonLegacy(ctx, node.x, node.y, r, 8)
-          ctx.setFillStyle(this.themeColor + '22')
+          ctx.fillStyle = hexToRgba(this.themeColor, 0.13)
           ctx.fill()
         } else {
           ctx.beginPath()
           ctx.arc(node.x, node.y, r, 0, 2 * Math.PI)
-          ctx.setFillStyle(this.themeColor + '22')
+          ctx.fillStyle = hexToRgba(this.themeColor, 0.13)
           ctx.fill()
         }
 
-        ctx.setFillStyle(node.isSelf ? this.themeColor : '#4A5568')
-        ctx.setFont('bold ' + (r * 0.9) + 'px sans-serif')
-        ctx.setTextAlign('center')
-        ctx.setTextBaseline('middle')
+        ctx.fillStyle = node.isSelf ? this.themeColor : '#4A5568'
+        ctx.font = 'bold ' + (r * 0.9) + 'px sans-serif'
+        ctx.textAlign = 'center'
+        ctx.textBaseline = 'middle'
         ctx.fillText(node.displayName, node.x, node.y)
-        ctx.setFillStyle(node.isSelf ? this.themeColor : '#94A3B8')
-        ctx.setFont((r * 0.4) + 'px sans-serif')
+        ctx.fillStyle = node.isSelf ? this.themeColor : '#94A3B8'
+        ctx.font = (r * 0.4) + 'px sans-serif'
         ctx.fillText(node.nickname.substring(0, 3), node.x, node.y + r + 10)
       }
       ctx.draw()