Страницы

Поиск по вопросам

воскресенье, 29 марта 2020 г.

Мини графический редактор на canvas и vuex

#javascript #vuejs #инспекция_кода #vuex


Сделал небольшой редактор изображений для изучения возможностей vuex с состояниями. 



Никто не хочет в рамках инспекции кода высказать свои замечания к написанному?

Файл components/Main.vue








Файл store/index.js

import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex)

const ModeEnum = Object.freeze({
  "PointerTool": "Pointer",

  "LineTool": "Line (click canvas to choose start point)",
  "LineToolStartPoint": "Line (click canvas to choose finish point)",

  "CircleTool": "Circle (click canvas to choose center)",
  "CircleToolCenterPoint": "Circle (choose radius)",

  "RectangleTool": "Rectangle (click canvas to set top left corner)",
  "RectangleToolTopLeftCorner": "Rectangle (click canvas to set bottom right corner)",
});

const state = {
  cursor: {
    X: 0,
    Y: 0,
  },
  temporaryPoint: {
    X: 0,
    Y: 0,
  },
  currentMode: ModeEnum.PointerTool,
}

const mutations = {
  coord(state, pos) {
    state.cursor.X = pos.X;
    state.cursor.Y = pos.Y;
  },

  setMode(state, mode) {
    state.currentMode = mode;
  },

  savePointCoordinates(state, pos) {
    state.temporaryPoint.X = pos.X;
    state.temporaryPoint.Y = pos.Y;
  },

  drawLine(state, pos) {
    var canvas = document.getElementById("cnv");
    var context = canvas.getContext("2d");

    context.beginPath();
    context.moveTo(state.temporaryPoint.X, state.temporaryPoint.Y);
    context.lineTo(pos.X, pos.Y);
    context.stroke();
  },

  drawCircle(state, pos) {
    var canvas = document.getElementById("cnv");
    var context = canvas.getContext("2d");

    context.beginPath();
    var radius = Math.sqrt((state.temporaryPoint.X - pos.X) * (state.temporaryPoint.X
- pos.X) + (state.temporaryPoint.Y - pos.Y) * (state.temporaryPoint.Y - pos.Y));
    context.arc(state.temporaryPoint.X, state.temporaryPoint.Y, radius, 0, Math.PI
* 2, false);
    context.strokeStyle = "red";
    context.stroke();
  },

  drawRectangle(state, pos) {
    var canvas = document.getElementById("cnv");
    var context = canvas.getContext("2d");

    context.beginPath();
    context.rect(state.temporaryPoint.X, state.temporaryPoint.Y, pos.X, pos.Y);
    context.stroke();
  },
}

const actions = {
  coord({
    commit
  }, event) {
    var canvas = document.getElementById("cnv");
    var rect = canvas.getBoundingClientRect();
    var pos = {
      X: event.clientX - rect.left,
      Y: event.clientY - rect.top,
    };

    commit('coord', pos);
  },

  setPointerMode: ({
    commit
  }) => commit('setMode', ModeEnum.PointerTool),

  setLineMode: ({
    commit
  }) => commit('setMode', ModeEnum.LineTool),

  setCircleMode: ({
    commit
  }) => commit('setMode', ModeEnum.CircleTool),

  setRectangleMode: ({
    commit
  }) => commit('setMode', ModeEnum.RectangleTool),

  canvas_left_click({
    commit
  }, event) {
    var canvas = document.getElementById("cnv");
    var rect = canvas.getBoundingClientRect();
    var pos = {
      X: event.clientX - rect.left,
      Y: event.clientY - rect.top,
    };

    switch (state.currentMode) {
      case ModeEnum.PointerTool:
        console.log('Current state is: ' + state.currentMode);
        break;

      case ModeEnum.LineTool:
        commit('savePointCoordinates', pos);
        commit('setMode', ModeEnum.LineToolStartPoint);
        break;

      case ModeEnum.LineToolStartPoint:
        commit('drawLine', pos);
        commit('setMode', ModeEnum.LineTool);
        break;

      case ModeEnum.CircleTool:
        commit('savePointCoordinates', pos);
        commit('setMode', ModeEnum.CircleToolCenterPoint);
        break;

      case ModeEnum.CircleToolCenterPoint:
        commit('drawCircle', pos);
        commit('setMode', ModeEnum.CircleTool);
        break;

      case ModeEnum.RectangleTool:
        commit('savePointCoordinates', pos);
        commit('setMode', ModeEnum.RectangleToolTopLeftCorner);
        break;

      case ModeEnum.RectangleToolTopLeftCorner:
        commit('drawRectangle', pos);
        commit('setMode', ModeEnum.RectangleTool);
        break;

      default:
        break;
    }
  },

  destroy() {
    var main = document.getElementById("cnv");
    main.ctx = main.getContext("2d");
    main.ctx.clearRect(0, 0, 600, 300);
  },
}

const getters = {}

export default new Vuex.Store({
  state,
  getters,
  actions,
  mutations
})


Мои собственные заметки:


мне не нравится, что объект canvas фактически не является состоянием. Фактически
приходящие команды рисования изменяют его состояние, верно? Тогда можно было бы вынести
процедуру получения контекста
нормально ли сохранять промежуточные клики (промежуточные переменные между событиями)
во временную переменную, возможно есть какой-то более изящный способ? 

    


Ответы

Ответ 1



Store предназначен только для хранения данных, которые нужны нескольким компонентам. Все, что касается только лишь одного компонента, должно находиться в компонента. Т.е., все методы, которые касаются отрисовки, я бы перенес непосредственно в компонент. Context, соответственно тоже может храниться в компоненте, и тогда не будет необходимости получать его каждый раз. Actions - предназначены для асинхронной работы с хранилищем. Те методы, которые используются у Вас в actions этому не соответствуют. Поэтому я бы их тоже перенес в компонент. Хотя... при работе с canvas, огромные функции, которые управляют холстом я выношу в отдельные файлы и просто импортирую их в компонент. Что касается моделуй в Store, то у меня это выглядит так: файл modules/index.js import person from './person'; import preview from './preview'; import grid from './grid'; import header from './header'; import bigData from './bigData'; import loading from './loading'; export default { person, preview, grid, header, bigData, loading, }; файл index.js import Vue from 'vue'; import Vuex from 'vuex'; import modules from './modules'; Vue.use(Vuex); const store = new Vuex.Store({ modules, }); export default store; как пример, modules/loading.js Модуль отвечает за отображание лоадера при запросах на сервер. const loading = { state: { isLoading: false, }, mutations: { SHOW_LOADING: state => { state.isLoading = true; }, HIDE_LOADING: state => { state.isLoading = false; }, }, }; export default loading; Но Vue очень гибок, и все что я сказал - это только лишь моя практика. Каждый может делать так, как ему будет удобно.

Комментариев нет:

Отправить комментарий