微信小程序绘制连线题(自参)

import util from '../../../../utils/util'

Component({
    
    
  /**
   * 组件的属性列表
   */
  properties: {
    
    
    questionSubject: {
    
    
      type: Object,
    },
  },

  observers: {
    
    
    questionSubject({
     
     content: {
     
      connectingLineList, optionList }}) {
    
    
      this.setData({
    
    
        leftData: connectingLineList,
        rightData: optionList
      });
      wx.nextTick(() => {
    
    
        this.getCanvas()
      })
    },
    context(val) {
    
    
      val && this.displayData()
    }
  },

  /**
   * 组件的初始数据
   */
  data: {
    
    
    leftActive: -1,
    rightActive: -1,
    leftPoint: new Map(),
    rightPoint: new Map(),
    leftData: [],
    rightData: [],
    myAnswer: new Map(),
    context: null,
    canvasHeight: 0,
    canvasWidth: 0,
    width: 0,
    height: 0,
    top: 0,
  },

  /**
   * 组件的方法列表
   */
  methods: {
    
    
    // 获取本地数据,有则回显
    async displayData() {
    
    
      let data = wx.getStorageSync('connectionAnswer')
      if (data) {
    
    
        const arr = data.filter(item => item[0] == this.data.questionSubject.questionTopicId)
        if (arr.length === 0) return
        const tempMap = new Map()
        this.setData({
    
    
          myAnswer: util.objToMap(arr[0][1], tempMap)
        })
        const list = this.data.questionSubject.answer.connectingLineList
        const newTempMap = this.addColorToAnswerData(tempMap, list)
        wx.nextTick(() => {
    
    
          this.rePaint(newTempMap)
        })
      } 
    },
 // 为提交的答案数据回显时添加正确错误的颜色差异
 addColorToAnswerData(map, list) {
    
    
   const newMap = new Map()
   for (const [key, val] of map) {
    
    
     val.forEach(id => {
    
    
       // green 正确 red 错误  includes为true代表有正确答案
       const color = list.find(item => item.prefix == key).answerPrefix.includes(id) ? 'green' : 'red'
       const oldVal = newMap.get(key) // 获取旧值重组数据项
       oldVal ? newMap.set(key, [...oldVal, {
    
     id, color }]) : newMap.set(key, [{
    
     id, color }])
     })
   }
   return newMap
 },
 // 提交答案
 handleBeSure() {
    
    
   const id = this.data.questionSubject.questionTopicId
   let data = wx.getStorageSync('connectionAnswer')
   if (!data) {
    
    
     data = []
   } else {
    
    
     // 把之前存储对应id的数据删除
     const index = data.findIndex(item => item[0] == id)
     index !== -1 && data.splice(index, 1)
   }
   let myAnswer = {
    
    }
   myAnswer = util.mapToObj(this.data.myAnswer, myAnswer) // 转换数据
   data.push([id, myAnswer]) // 添加
   wx.setStorageSync('connectionAnswer', data) // 存储
   this.triggerEvent('onSubmit')
 },
// 点击连线的两个元素
itemClick(e) {
    
    
      let {
    
    
        isleft,
        prefix,
      } = e.target.dataset;

      if (isleft) {
    
    
        this.data.leftActive = prefix
      } else {
    
    
        this.data.rightActive = prefix
      }

      // 当前点击的左右元素
      let leftActive = this.data.leftActive;
      let rightActive = this.data.rightActive;
      // 点击左右两边的按钮
      if ((leftActive !== -1) && (rightActive !== -1)) {
    
    
        const source = this.data.leftPoint.get(leftActive);
        const target = this.data.rightPoint.get(rightActive);
        const myAnswer = this.data.myAnswer;
        // 左边连接点是否已存储进myAnswer
        const existenceLeftActive = myAnswer.get(leftActive)
        if (existenceLeftActive !== undefined) {
    
    
          // length为0则表示左右两个点还没进行过连线,判断为false。
          if (existenceLeftActive.filter(item => item == rightActive).length) {
    
    
            myAnswer.set(leftActive, existenceLeftActive.filter(item => item != rightActive));
            // 重绘删除重复连接线
            this.rePaint(this.data.myAnswer)

            this.setData({
    
    
              leftActive: -1,
              rightActive: -1,
              myAnswer
            })
            return;
          }
          existenceLeftActive.push(rightActive);
          myAnswer.set(leftActive, existenceLeftActive)
        } else {
    
    
          // 存储连线的左右两个点
          myAnswer.set(leftActive, [rightActive])
        }

        const sy = source.y - this.data.top + (source.height / 2);
        const ty = target.y - this.data.top + (target.height / 2);
        const dpr = wx.getWindowInfo().pixelRatio;

        this.lineTo({
    
    
          x: 0,
          y: sy * dpr
        }, {
    
    
          x: this.data.width * dpr,
          y: ty * dpr
        })

        // 重置数据
        this.setData({
    
    
          leftActive: -1,
          rightActive: -1,
          myAnswer
        });
      }
    },
    // 重绘
    rePaint: function (myAnswer) {
    
    
      const dpr = wx.getWindowInfo().pixelRatio;
      const context = this.data.context;
      
      context.clearRect(0, 0, this.data.width * dpr, this.data.height * dpr); // 清空线条
      for (let [key, val] of myAnswer) {
    
    
        val.forEach(point => {
    
    
          const source = this.data.leftPoint.get(key);
          const target = this.data.rightPoint.get(point.id ?? point);
          const sy = source.y - this.data.top + (source.height / 2);
          const ty = target.y - this.data.top + (target.height / 2);
          this.lineTo({
    
    
            x: 0,
            y: sy * dpr
          }, {
    
    
            x: this.data.width * dpr,
            y: ty * dpr
          }, point.color ?? 'blue')
        });
      }
    },
    // 绘画
    lineTo(source, target, color = 'blue') {
    
    
      const context = this.data.context;
      if (context) {
    
    
        context.beginPath(); // beginPath() 方法开始一条路径,或重置当前的路径。
        context.moveTo(source.x, source.y); //起始位置
        context.lineTo(target.x, target.y); //终止位置
        context.lineWidth = '3' //线段宽
        context.strokeStyle = color //线段颜色
        context.stroke();
        context.closePath();
      }
    },
    // 准备连线位置数据
    getCanvas() {
    
    
      this.setData({
    
    
        canvasHeight: 0,
        myAnswer: new Map()
      })
      // canvas宽高
      const query = this.createSelectorQuery();
      query.select('#matching-box').boundingClientRect();
      query.exec((res) => {
    
    
        const dpr = wx.getWindowInfo().pixelRatio
        
        this.setData({
    
    
          canvasWidth: res[0].width * dpr,
          canvasHeight: res[0].height * dpr - 200
        });
        
        const width = res[0].width * dpr;
        // 获取左侧按钮位置
        const leftPoint = new Map();
        this.data.leftData.forEach(item => {
    
    
          const query = this.createSelectorQuery();
          query.select('#left' + item.prefix).boundingClientRect();
          query.exec((res) => {
    
    
            leftPoint.set(item.prefix, {
    
    
              x: 0,
              y: res[0].top,
              height: res[0].height
            })
          });
        })

        // 获取右侧按钮位置
        const rightPoint = new Map();
        this.data.rightData.forEach(item => {
    
    
          const query = this.createSelectorQuery();
          query.select('#right' + item.prefix).boundingClientRect();
          query.exec((res) => {
    
    
            rightPoint.set(item.prefix, {
    
    
              x: width,
              y: res[0].top,
              height: res[0].height
            })
          });
        })
        this.setData({
    
    
          rightPoint,
          leftPoint
        });

        this.createSelectorQuery().select('#canvas')
          .fields({
    
    
            node: true,
            size: true
          })
          .exec((res) => {
    
    
            const {
    
    
              node,
              width,
              height
            } = res[0];
            if (!node) return
            /* 获取 canvas 实例 */
            const dpr = wx.getSystemInfoSync().pixelRatio;
            node.width = res[0].width * dpr;
            node.height = res[0].height * dpr;
            const context = node.getContext('2d');
            // canvas宽高
            this.setData({
    
    
              context,
              width,
              height
            })
          });
        this.createSelectorQuery().select('#matching-box').boundingClientRect().exec((res) => {
    
    
          const {
    
    
            top
          } = res[0];
          // 距离顶部高度
          this.setData({
    
    
            top
          })
        })
      });
    },

父组件

// 对连线题答案数据翻译成id对应的内容进行页面显示 
    formatConnectionQuestionAnswer(subject, list) {
    
    
      list.connectingLineList.map(item => {
    
    
        const {
    
     content } = subject.content.connectingLineList.find(left => left.prefix === item.prefix)
        item.formatPrefix = content
        item.formatAnswerPrefix = []
        item.answerPrefix.forEach(answerRight => {
    
    
          const {
    
     content } = subject.content.optionList.find(right => right.prefix === answerRight)
          item.formatAnswerPrefix.push(content)
        })
      })
    },
// 连线题提交答案
    connectionQuestionSumbit() {
    
    
      const {
    
     questionMainId, questionTopicId, questionVodUserRecordId } = this.data.questionSubject
      this.setData({
    
    
        questionMainId,
        questionTopicId,
        questionVodUserRecordId
    });
      const connectionQuestionRef = this.selectComponent('#connection-question')
      let submitAnswer = {
    
    
        connectingLineList: [],
        resourceList: null
      }
      const answer = connectionQuestionRef.data.myAnswer
      for(const [key, value] of answer) {
    
    
        submitAnswer.connectingLineList.push({
    
    
          prefix: key,
          answerPrefix: value,
        })
      }
      this.questionVodUserSubmit(submitAnswer)
    },

utils

/**
 * Map数据转换Object,小程序本地不支持存储Map数据
 */
function mapToObj(map, obj = {
     
     }) {
    
    
  for (const [key, value] of map) {
    
    
    obj[key] = value
  }
  return obj
}
/**
 * Object数据转换Map
 */
function objToMap(obj, map) {
    
    
  for (const key in obj) {
    
    
    map.set(key, obj[key])
  }
  return map
}

猜你喜欢

转载自blog.csdn.net/qq_40230735/article/details/130482037