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

144
06 августа 2019, 23:50

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

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

Файл components/Main.vue

<template>
  <div class="Main">
    <div class="buttons_bar">
      <button v-on:click="setPointerMode" type="button" class="btn btn-light">Point tool</button>
      <button v-on:click="setLineMode" type="button" class="btn btn-light">Line tool</button>
      <button v-on:click="setCircleMode" type="button" class="btn btn-light">Circle tool</button>
      <button v-on:click="setRectangleMode" type="button" class="btn btn-light">Rectangle tool</button>
      <button v-on:click="destroy" type="button" class="btn btn-light">Clear tool</button>
    </div>
    <div>
      <canvas id="cnv" height="300" width="600" v-on:mousemove="coord" v-on:click="canvas_left_click"></canvas>
    </div>
    <div id="status_bar">
      <div>Mode: {{ $store.state.currentMode }}</div>
      <div>X: {{ $store.state.cursor.X }}</div>
      <div>Y: {{ $store.state.cursor.Y }}</div>
    </div>
  </div>
</template>
<script>
import { mapGetters, mapActions } from "vuex";
export default {
  name: "Main",
    methods: mapActions([
    'coord',
    'canvas_left_click',
    'setPointerMode',
    'setLineMode',
    'setCircleMode',
    'setRectangleMode',
    'destroy',
  ])
};
</script>
<style scoped>
.buttons_bar {
  border: 1px solid black;
  padding: 5px;
}
div > canvas {
  width: 600px;
  height: 300px;
  border: 1px solid red;
}
</style>

Файл 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 фактически не является состоянием. Фактически приходящие команды рисования изменяют его состояние, верно? Тогда можно было бы вынести процедуру получения контекста
  • нормально ли сохранять промежуточные клики (промежуточные переменные между событиями) во временную переменную, возможно есть какой-то более изящный способ?
Answer 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 очень гибок, и все что я сказал - это только лишь моя практика. Каждый может делать так, как ему будет удобно.

READ ALSO
На чем пишут калькулятор доставки? [закрыт]

На чем пишут калькулятор доставки? [закрыт]

Скорее всего скрипт, но как понимаю, есть варианты с использованием обращений к БД, либо без негоХотелось бы конечно без, но в чем плюсы с БД и различия...

148
Обработка div&#39;a циклом в Javascript

Обработка div'a циклом в Javascript

Есть кусок кода сайта

141
Можно ли ? ::before ::arfter

Можно ли ? ::before ::arfter

Подскажите, можно ли достучатся из js к псевдоэлементам DOMa?

117