diff --git a/frontend/src/assets/template/make_a_wish_tour_social_a2/background.png b/frontend/src/assets/template/make_a_wish_tour_social_a2/background.png new file mode 100644 index 0000000000000000000000000000000000000000..e5e6e89f3134d464bb642e003757217d647258ca Binary files /dev/null and b/frontend/src/assets/template/make_a_wish_tour_social_a2/background.png differ diff --git a/frontend/src/templates.js b/frontend/src/templates.js index c210eca30a13c80e01ebee7e76fbe16b4e86e992..592222251daacd2f35aecf89946706b3c3aedfc7 100644 --- a/frontend/src/templates.js +++ b/frontend/src/templates.js @@ -67,6 +67,15 @@ const TEMPLATES = { title: "Máte přání tour banner na soc. sítě", }, }, + make_a_wish_tour_social_a2: { + name: "A2 - Máte přání tour banner na soc. sítě", + image: makeawishTourSocialImage, + path: "/make-a-wish-tour-social-a2", + component: () => import("./views/make_a_wish_tour_social_A2/MakeAWishTourSocialA2.vue"), + meta: { + title: "A2 - Máte přání tour banner na soc. sítě", + }, + }, back_in_full_force_banner: { name: "Zpátky v plné síle banner", image: backInFullForcePhotoBannerImage, @@ -85,6 +94,15 @@ const TEMPLATES = { title: "Zpátky v plné síle tour banner na soc. sítě", }, }, + back_in_full_force_tour_social_A2: { + name: "A2 - Zpátky v plné síle tour banner na soc. sítě", + image: backInFullForceTourSocialImage, + path: "/back-in-full-force-tour-social-a2", + component: () => import("./views/back_in_full_force_tour_social_A2/BackInFullForceTourSocialA2.vue"), + meta: { + title: "A2 - Zpátky v plné síle tour banner na soc. sítě", + }, + }, urgent_text_banner: { name: "Urgentní banner pouze s textem", image: urgentTextBannerImage, diff --git a/frontend/src/views/back_in_full_force_tour_social_A2/BackInFullForceTourSocialA2.vue b/frontend/src/views/back_in_full_force_tour_social_A2/BackInFullForceTourSocialA2.vue new file mode 100644 index 0000000000000000000000000000000000000000..a9e4bccbe3bc09d8a61780cda2f1cea9732525dd --- /dev/null +++ b/frontend/src/views/back_in_full_force_tour_social_A2/BackInFullForceTourSocialA2.vue @@ -0,0 +1,255 @@ +<script setup> +import { watch, ref } from "vue"; + +import COLORS from "../../colors"; +import PEOPLE from "../../people"; +import TEMPLATES from "../../templates"; +import DEFAULT_CONTRACTOR from "../../contractors"; +import { + loadFonts, + loadCanvasStorage, + setCanvasStorage, + updateAutoRedrawStorage, +} from "../../utils"; + +import Canvas from "../../components/canvas/Canvas.vue"; +import redraw from "./canvas"; + +import Navbar from "../../components/Navbar.vue"; +import MainContainer from "../../components/MainContainer.vue"; +import ImageInput from "../../components/inputs/ImageInput.vue"; +import LongTextInput from "../../components/inputs/text/LongTextInput.vue"; +import ShortTextInput from "../../components/inputs/text/ShortTextInput.vue"; +import RangeInput from "../../components/inputs/RangeInput.vue"; +import InputSeparator from "../../components/inputs/InputSeparator.vue"; +import SelectInput from "../../components/inputs/SelectInput.vue"; +import MultipleColorPicker from "../../components/inputs/colors/MultipleColorPicker.vue"; +import ReloadButton from "../../components/reload/ReloadButton.vue"; +import AutoReloadCheckbox from "../../components/reload/AutoReloadCheckbox.vue"; +</script> + +<script> +await loadFonts([ + "12px Bebas Neue", + "12px Roboto Condensed", + "bold 12px Roboto Condensed", +]); + +export default { + components: { + Canvas, + Navbar, + MainContainer, + ImageInput, + LongTextInput, + ShortTextInput, + RangeInput, + SelectInput, + InputSeparator, + MultipleColorPicker, + }, + data() { + const predefinedColors = { + base: { + name: "Černé pozadí", + colors: { + background: COLORS.black, + highlight: COLORS.yellow1, + baseText: COLORS.white, + nameText: COLORS.yellow1, + highlightedText: COLORS.black, + contractedByText: COLORS.gray1, + }, + }, + white: { + name: "Bílé pozadí", + colors: { + background: COLORS.white, + highlight: COLORS.yellow1, + baseText: COLORS.black, + nameText: COLORS.black, + highlightedText: COLORS.black, + contractedByText: COLORS.gray1, + }, + }, + }; + + return { + mainImage: null, + mainText: null, + + dateText: null, + timeText: null, + locationText: null, + attendeesText: null, + + personName: null, + personPosition: null, + contractedBy: DEFAULT_CONTRACTOR, + colorLabels: { + background: "Pozadí", + highlight: "Zvýraznění", + baseText: "Město", + highlightedText: "Zvýrazněný text", + }, + predefinedColors: predefinedColors, + colors: predefinedColors.base.colors, + autoRedraw: false, + }; + }, + async created() { + await loadCanvasStorage(this); + }, + methods: { + async reloadCanvasProperties() { + const canvasProperties = { + mainImage: this.mainImage, + mainText: this.mainText, + + dateText: this.dateText, + timeText: this.timeText, + locationText: this.locationText, + attendeesText: this.attendeesText, + + personName: this.personName, + personPosition: this.personPosition, + contractedBy: this.contractedBy, + colors: this.colors, + }; + + if (canvasProperties.mainText) { + window.fileName = canvasProperties.mainText; + } + + await this.$refs.canvas.redraw(canvasProperties); + + delete canvasProperties.colors; + setCanvasStorage(canvasProperties); + }, + }, + mounted() { + this.$watch( + (vm) => [ + vm.mainImage, + vm.mainText, + vm.dateText, + vm.timeText, + vm.locationText, + vm.attendeesText, + vm.personName, + vm.personPosition, + vm.contractedBy, + vm.colors, + ], + async (value) => { + if (this.autoRedraw) { + await this.reloadCanvasProperties(); + } + }, + { + immediate: true, + deep: true, + }, + ); + + this.$watch( + (vm) => [vm.autoRedraw], + async (value) => { + updateAutoRedrawStorage(this.autoRedraw); + + if (this.autoRedraw) { + await this.reloadCanvasProperties(); + } + }, + ); + }, +}; +</script> + +<template> + <header> + <Navbar :defaultTemplate="TEMPLATES.basic_photo_banner"></Navbar> + </header> + <main> + <MainContainer> + <template v-slot:left> + <Canvas + ref="canvas" + :redrawFunction="redraw" + width="2000" + height="2000" + /> + </template> + + <template v-slot:right> + <ReloadButton :parentRefs="$refs" @click="reloadCanvasProperties" /> + <AutoReloadCheckbox v-model="autoRedraw" /> + <ImageInput + name="Obrázek" + v-model="mainImage" + :important="true" + zIndex="10" + /> + <LongTextInput + name="Město konání akce" + v-model="mainText" + :important="true" + :highlightable="true" + zIndex="9" + /> + + <ShortTextInput + name="Datum akce" + v-model="dateText" + :important="true" + zIndex="8" + /> + + <ShortTextInput + name="Čas akce" + v-model="timeText" + :important="true" + zIndex="7" + /> + + <LongTextInput + name="Účastníci akce" + v-model="attendeesText" + :important="true" + zIndex="6" + /> + + <LongTextInput + name="Přesné místo konání akce" + v-model="locationText" + :important="true" + zIndex="6" + /> + + <InputSeparator /> + + <MultipleColorPicker + name="Barvy" + v-model="colors" + :important="false" + :colorLabels="colorLabels" + :predefinedColors="predefinedColors" + :defaultPredefinedColors="predefinedColors.base" + zIndex="5" + ></MultipleColorPicker> + + <ShortTextInput + name="Zadavatel a zpracovatel" + v-model="contractedBy" + :defaultValue="DEFAULT_CONTRACTOR" + :important="false" + zIndex="4" + /> + </template> + </MainContainer> + </main> +</template> + +<style> +@import "vue-select/dist/vue-select.css"; +</style> diff --git a/frontend/src/views/back_in_full_force_tour_social_A2/canvas.js b/frontend/src/views/back_in_full_force_tour_social_A2/canvas.js new file mode 100644 index 0000000000000000000000000000000000000000..735fa7db5fd22b8c7067261d39e512a6437a60b8 --- /dev/null +++ b/frontend/src/views/back_in_full_force_tour_social_A2/canvas.js @@ -0,0 +1,465 @@ +import { fabric } from "fabric"; +import { + clearObjects, + sortObjects, + transformHighlightedText, + checkTextBoxHeight, +} from "../../components/canvas/utils"; +import { PaddedHighlightingTextbox } from "../../components/canvas/textbox"; +import backgroundURL from "../../assets/template/back_in_full_force_tour_social/background.png"; +import backgroundURLInverted from "../../assets/template/make_a_wish_banner/background_inverted.png"; +import COLORS from "../../colors"; + +let mainTextBox = null; + +let attendeesTextBox = null; +let dateTextBox = null; +let timeTextBox = null; +let locationTextBox = null; + +let mainImage = null; +let backgroundImage = null; +let previousBackgroundImageColor = null; + +let contractedByTextbox = null; + +let mainImageSource = null; + +const removeDownEventListener = () => { + document + .getElementsByClassName("upper-canvas")[0] + .removeEventListener("pointerdown", canvasPointerDownEvent); +}; + +let upEventFunction = null; +let canvasPointerDownEvent = null; +let pointerDownEventAssigned = false; + +const redraw = async (canvas, options) => { + clearObjects( + [ + mainTextBox, + contractedByTextbox, + attendeesTextBox, + dateTextBox, + timeTextBox, + locationTextBox + ], + canvas, + ); + + document + .getElementsByClassName("upper-canvas")[0] + .removeEventListener("pointerup", upEventFunction); + document + .getElementsByClassName("upper-canvas")[0] + .removeEventListener("pointerout", upEventFunction); + document + .getElementsByClassName("upper-canvas")[0] + .removeEventListener("pointercancel", upEventFunction); + + canvas.preserveObjectStacking = true; + + const textMarginLeft = Math.ceil(canvas.width * 0.1); + const textMarginRight = Math.ceil(canvas.width * 0.078); + + let mainTextMarginBottom = Math.ceil(canvas.height * 0.35) + Math.ceil(canvas.height * 0.15); + const dateTextMarginBottom = Math.ceil(canvas.height * 0.63) + Math.ceil(canvas.height * 0.25); + const timeTextBoxMarginBottom = Math.ceil(canvas.height * 0.565) + Math.ceil(canvas.height * 0.235); + const locationTextBoxMarginBottom = Math.ceil(canvas.height * 0.32) + Math.ceil(canvas.height * 0.14); + const attendeesTextBoxMarginBottom = Math.ceil(canvas.height * 0.1) + Math.ceil(canvas.height * 0.11); + + const mainTextSize = Math.ceil(canvas.height * 0.185); + const mainTextLineHeight = 1; + + const dateTextSize = Math.ceil(canvas.height * 0.055); + const timeTextSize = Math.ceil(canvas.height * 0.05); + const attendeesTextSize = Math.ceil(canvas.height * 0.04); + const attendeesTextLineHeight = 1; + + const locationTextSize = Math.ceil(canvas.height * 0.055); + const locationTextLineHeight = 1; + + const contractedByTextSize = Math.ceil(canvas.height * 0.01); + const contractedByTextMaxWidth = Math.ceil(canvas.width * 0.9); + const contractedByTextSidesMargin = Math.ceil(canvas.width * 0.03); + + if (options.dateText !== null) { + /* BEGIN Date text render */ + + const dateTextWidth = canvas.width; + + console.log(options); + + const highlightedData = transformHighlightedText( + options.dateText, + mainTextSize, + dateTextWidth, + "Bebas Neue", + options.colors.highlightedText.value, + options.colors.highlight.value, + { padWhenDiacritics: false, invertHighlight: true }, + ); + + dateTextBox = new PaddedHighlightingTextbox(highlightedData.text, { + width: canvas.width, + left: 0, + textAlign: "right", + fontFamily: "Bebas Neue", + fontSize: dateTextSize, + fill: options.colors.baseText.value, + styles: highlightedData.styles, + selectable: false, + highlightPadding: canvas.height * 0.003, + zIndex: 20, + }); + + checkTextBoxHeight(dateTextBox, 1); + + canvas.add(dateTextBox); + + const dateTextBoxTop = + canvas.height - dateTextBox.height - dateTextMarginBottom; + + dateTextBox.top = dateTextBoxTop - highlightedData.paddingBottom; + + const dateTextBoxLeft = + canvas.width - dateTextBox.width - textMarginRight; + + dateTextBox.left = dateTextBoxLeft; + + canvas.renderAll(); + + /* END Date text render */ + } + + if (options.timeText !== null) { + /* BEGIN Time text render */ + + const timeTextWidth = canvas.width; + + const highlightedData = transformHighlightedText( + options.timeText, + timeTextSize, + timeTextWidth, + "Bebas Neue", + options.colors.highlightedText.value, + options.colors.highlight.value, + { padWhenDiacritics: false, invertHighlight: true }, + ); + + timeTextBox = new PaddedHighlightingTextbox(highlightedData.text, { + width: canvas.width, + left: 0, + textAlign: "right", + fontFamily: "Bebas Neue", + fontSize: timeTextSize, + fill: options.colors.baseText.value, + styles: highlightedData.styles, + selectable: false, + highlightPadding: canvas.height * 0.003, + zIndex: 20, + }); + + checkTextBoxHeight(timeTextBox, 1); + + canvas.add(timeTextBox); + + const timeTextBoxTop = + canvas.height - timeTextBox.height - timeTextBoxMarginBottom; + + timeTextBox.top = timeTextBoxTop - highlightedData.paddingBottom; + + const timeTextBoxLeft = + canvas.width - timeTextBox.width - textMarginRight; + + timeTextBox.left = timeTextBoxLeft; + + canvas.renderAll(); + + /* END Time text render */ + } + + if (options.locationText !== null) { + /* BEGIN Location text render */ + + const locationTextWidth = canvas.width - textMarginLeft - textMarginRight; + + const highlightedData = transformHighlightedText( + options.locationText, + locationTextSize, + locationTextWidth, + "Bebas Neue", + options.colors.highlightedText.value, + options.colors.highlight.value, + { padWhenDiacritics: false, invertHighlight: true }, + ); + + locationTextBox = new PaddedHighlightingTextbox(highlightedData.text, { + width: canvas.width, + left: 0, + textAlign: "right", + fontFamily: "Bebas Neue", + fontSize: locationTextSize, + fill: options.colors.baseText.value, + styles: highlightedData.styles, + lineHeight: locationTextLineHeight, + selectable: false, + highlightPadding: canvas.height * 0.003, + zIndex: 20, + }); + + checkTextBoxHeight(locationTextBox, 2); + + canvas.add(locationTextBox); + + canvas.renderAll(); + + const locationTextBoxTop = + canvas.height - locationTextBox.height - locationTextBoxMarginBottom + (locationTextSize * locationTextLineHeight * (locationTextBox.textLines.length - 1)); + + locationTextBox.top = locationTextBoxTop - highlightedData.paddingBottom; + + const locationTextBoxLeft = + canvas.width - locationTextBox.width - textMarginRight; + + locationTextBox.left = locationTextBoxLeft; + + canvas.renderAll(); + + /* END Location text render */ + } + + if (options.attendeesText !== null) { + /* BEGIN Attendees text render */ + + const attendeesTextWidth = canvas.width - textMarginLeft - textMarginRight; + + const highlightedData = transformHighlightedText( + options.attendeesText, + attendeesTextSize, + attendeesTextWidth, + "Roboto Condensed", + options.colors.highlightedText.value, + options.colors.highlight.value, + { padWhenDiacritics: false, invertHighlight: true }, + ); + + attendeesTextBox = new PaddedHighlightingTextbox(highlightedData.text, { + width: canvas.width, + left: 0, + textAlign: "right", + fontFamily: "Roboto Condensed", + fontSize: attendeesTextSize, + fill: options.colors.baseText.value, + styles: highlightedData.styles, + selectable: false, + highlightPadding: canvas.height * 0.003, + zIndex: 20, + }); + + checkTextBoxHeight(attendeesTextBox, 3); + + canvas.add(attendeesTextBox); + + const attendeesTextBoxTop = + canvas.height - attendeesTextBox.height - attendeesTextBoxMarginBottom + (attendeesTextSize * attendeesTextLineHeight * (attendeesTextBox.textLines.length - 1)) + ((3 - attendeesTextBox.textLines.length - 1) * attendeesTextSize * attendeesTextLineHeight); + + attendeesTextBox.top = attendeesTextBoxTop - highlightedData.paddingBottom; + + const attendeesTextBoxLeft = + canvas.width - attendeesTextBox.width - textMarginRight; + + attendeesTextBox.left = attendeesTextBoxLeft; + + canvas.renderAll(); + + /* END Attendees text render */ + } + + if (options.mainText !== null) { + /* BEGIN Background render */ + + if (backgroundImage === null || options.colors.background.value != previousBackgroundImageColor.value || !canvas.getObjects().includes(backgroundImage)) { + backgroundImage = new Image(); + + await new Promise((resolve) => { + backgroundImage.onload = () => { + resolve(); + }; + + if (options.colors.background.value == COLORS.black.value) { + backgroundImage.src = backgroundURL; + } else { + backgroundImage.src = backgroundURLInverted; + } + }); + + backgroundImage = new fabric.Image(backgroundImage, { + top: 0, + left: 0, + zIndex: 10, + selectable: false, + }); + backgroundImage.scaleToWidth(canvas.width); + + previousBackgroundImageColor = options.colors.background; + + canvas.add(backgroundImage); + } + + /* END Background render */ + + + /* BEGIN Main text render */ + + const mainTextWidth = canvas.width - textMarginLeft - textMarginRight; + + const highlightedData = transformHighlightedText( + options.mainText, + mainTextSize, + mainTextWidth, + "Bebas Neue", + options.colors.highlightedText.value, + options.colors.highlight.value, + { padWhenDiacritics: false, invertHighlight: true }, + ); + + mainTextBox = new PaddedHighlightingTextbox(highlightedData.text, { + width: canvas.width, + left: 0, + textAlign: "right", + fontFamily: "Bebas Neue", + fontSize: mainTextSize, + lineHeight: mainTextLineHeight, + fill: options.colors.highlight.value, + styles: highlightedData.styles, + selectable: false, + highlightPadding: canvas.height * 0.003, + zIndex: 20, + }); + + checkTextBoxHeight(mainTextBox, 1); + + canvas.add(mainTextBox); + + const mainTextBoxTop = + canvas.height - mainTextBox.height - mainTextMarginBottom; + + mainTextBox.top = mainTextBoxTop - highlightedData.paddingBottom; + + const mainTextBoxLeft = + canvas.width - mainTextBox.width - textMarginRight; + + mainTextBox.left = mainTextBoxLeft; + + canvas.renderAll(); + + /* END Main text render */ + } + + /* BEGIN Contracted by render */ + + if (options.contractedBy !== null) { + contractedByTextbox = new fabric.Textbox(options.contractedBy, { + left: contractedByTextSidesMargin, + top: contractedByTextSidesMargin, + width: contractedByTextMaxWidth, + fontFamily: "Roboto Condensed", + fontSize: contractedByTextSize, + textAlign: "left", + fill: options.colors.contractedByText.value, + selectable: false, + zIndex: 20, + }); + + checkTextBoxHeight(contractedByTextbox, 1); + + canvas.add(contractedByTextbox); + } + + /* END Contracted by render */ + + /* BEGIN Main image render */ + + if ( + options.mainImage !== null && + (!canvas.contains(mainImage) || + mainImage === null || + options.mainImage.src !== mainImageSource) + ) { + if (mainImage !== null) { + canvas.remove(mainImage); + } + + mainImage = new fabric.Image(options.mainImage, { + left: 0, + top: 0, + zIndex: 0, + }); + + mainImage.controls = { + ...fabric.Image.prototype.controls, + mtr: new fabric.Control({ visible: false }), + }; + + if (mainImage.width >= mainImage.height) { + mainImage.scaleToHeight(canvas.height); + } else { + mainImage.scaleToWidth(canvas.width); + } + + canvas.add(mainImage); + mainImageSource = options.mainImage.src; + // canvas.centerObject(mainImage) + } else if (mainImage !== null && options.mainImage === null) { + canvas.remove(mainImage); + } + + /* END Main image render */ + + sortObjects(canvas); + + canvasPointerDownEvent = (event) => { + let activeObject = canvas.getActiveObject(); + + if (activeObject === null) { + return; + } + + // if (activeObject._element.src == mainImage._element.src) { + // return + // } + + canvas.remove(backgroundImage); + backgroundImage = null; + }; + + if (!pointerDownEventAssigned) { + document + .getElementsByClassName("upper-canvas")[0] + .addEventListener("pointerdown", canvasPointerDownEvent); + + pointerDownEventAssigned = true; + } + + const colors = {...options.colors}; + + upEventFunction = (event) => { + redraw(canvas, {...options, colors: colors}); + }; + + document + .getElementsByClassName("upper-canvas")[0] + .addEventListener("pointerup", upEventFunction); + + document + .getElementsByClassName("upper-canvas")[0] + .addEventListener("pointerout", upEventFunction); + + document + .getElementsByClassName("upper-canvas")[0] + .addEventListener("pointercancel", upEventFunction); +}; + +export default redraw; diff --git a/frontend/src/views/make_a_wish_tour_social_A2/MakeAWishTourSocialA2.vue b/frontend/src/views/make_a_wish_tour_social_A2/MakeAWishTourSocialA2.vue new file mode 100644 index 0000000000000000000000000000000000000000..2ef67561a47b2b84c5aad02845610394879608a0 --- /dev/null +++ b/frontend/src/views/make_a_wish_tour_social_A2/MakeAWishTourSocialA2.vue @@ -0,0 +1,255 @@ +<script setup> +import { watch, ref } from "vue"; + +import COLORS from "../../colors"; +import PEOPLE from "../../people"; +import TEMPLATES from "../../templates"; +import DEFAULT_CONTRACTOR from "../../contractors"; +import { + loadFonts, + loadCanvasStorage, + setCanvasStorage, + updateAutoRedrawStorage, +} from "../../utils"; + +import Canvas from "../../components/canvas/Canvas.vue"; +import redraw from "./canvas"; + +import Navbar from "../../components/Navbar.vue"; +import MainContainer from "../../components/MainContainer.vue"; +import ImageInput from "../../components/inputs/ImageInput.vue"; +import LongTextInput from "../../components/inputs/text/LongTextInput.vue"; +import ShortTextInput from "../../components/inputs/text/ShortTextInput.vue"; +import RangeInput from "../../components/inputs/RangeInput.vue"; +import InputSeparator from "../../components/inputs/InputSeparator.vue"; +import SelectInput from "../../components/inputs/SelectInput.vue"; +import MultipleColorPicker from "../../components/inputs/colors/MultipleColorPicker.vue"; +import ReloadButton from "../../components/reload/ReloadButton.vue"; +import AutoReloadCheckbox from "../../components/reload/AutoReloadCheckbox.vue"; +</script> + +<script> +await loadFonts([ + "12px Bebas Neue", + "12px Roboto Condensed", + "bold 12px Roboto Condensed", +]); + +export default { + components: { + Canvas, + Navbar, + MainContainer, + ImageInput, + LongTextInput, + ShortTextInput, + RangeInput, + SelectInput, + InputSeparator, + MultipleColorPicker, + }, + data() { + const predefinedColors = { + base: { + name: "Černé pozadí", + colors: { + background: COLORS.black, + highlight: COLORS.yellow1, + baseText: COLORS.white, + nameText: COLORS.yellow1, + highlightedText: COLORS.black, + contractedByText: COLORS.gray1, + }, + }, + white: { + name: "Bílé pozadí", + colors: { + background: COLORS.white, + highlight: COLORS.yellow1, + baseText: COLORS.black, + nameText: COLORS.black, + highlightedText: COLORS.black, + contractedByText: COLORS.gray1, + }, + }, + }; + + return { + mainImage: null, + mainText: null, + + dateText: null, + timeText: null, + locationText: null, + attendeesText: null, + + personName: null, + personPosition: null, + contractedBy: DEFAULT_CONTRACTOR, + colorLabels: { + background: "Pozadí", + highlight: "Zvýraznění", + baseText: "Město", + highlightedText: "Zvýrazněný text", + }, + predefinedColors: predefinedColors, + colors: predefinedColors.base.colors, + autoRedraw: false, + }; + }, + async created() { + await loadCanvasStorage(this); + }, + methods: { + async reloadCanvasProperties() { + const canvasProperties = { + mainImage: this.mainImage, + mainText: this.mainText, + + dateText: this.dateText, + timeText: this.timeText, + locationText: this.locationText, + attendeesText: this.attendeesText, + + personName: this.personName, + personPosition: this.personPosition, + contractedBy: this.contractedBy, + colors: this.colors, + }; + + if (canvasProperties.mainText) { + window.fileName = canvasProperties.mainText; + } + + await this.$refs.canvas.redraw(canvasProperties); + + delete canvasProperties.colors; + setCanvasStorage(canvasProperties); + }, + }, + mounted() { + this.$watch( + (vm) => [ + vm.mainImage, + vm.mainText, + vm.dateText, + vm.timeText, + vm.locationText, + vm.attendeesText, + vm.personName, + vm.personPosition, + vm.contractedBy, + vm.colors, + ], + async (value) => { + if (this.autoRedraw) { + await this.reloadCanvasProperties(); + } + }, + { + immediate: true, + deep: true, + }, + ); + + this.$watch( + (vm) => [vm.autoRedraw], + async (value) => { + updateAutoRedrawStorage(this.autoRedraw); + + if (this.autoRedraw) { + await this.reloadCanvasProperties(); + } + }, + ); + }, +}; +</script> + +<template> + <header> + <Navbar :defaultTemplate="TEMPLATES.basic_photo_banner"></Navbar> + </header> + <main> + <MainContainer> + <template v-slot:left> + <Canvas + ref="canvas" + :redrawFunction="redraw" + width="2480" + height="3580" + /> + </template> + + <template v-slot:right> + <ReloadButton :parentRefs="$refs" @click="reloadCanvasProperties" /> + <AutoReloadCheckbox v-model="autoRedraw" /> + <ImageInput + name="Obrázek" + v-model="mainImage" + :important="true" + zIndex="10" + /> + <LongTextInput + name="Město konání akce" + v-model="mainText" + :important="true" + :highlightable="true" + zIndex="9" + /> + + <ShortTextInput + name="Datum akce" + v-model="dateText" + :important="true" + zIndex="8" + /> + + <ShortTextInput + name="Čas akce" + v-model="timeText" + :important="true" + zIndex="7" + /> + + <LongTextInput + name="Účastníci akce" + v-model="attendeesText" + :important="true" + zIndex="6" + /> + + <LongTextInput + name="Přesné místo konání akce" + v-model="locationText" + :important="true" + zIndex="6" + /> + + <InputSeparator /> + + <MultipleColorPicker + name="Barvy" + v-model="colors" + :important="false" + :colorLabels="colorLabels" + :predefinedColors="predefinedColors" + :defaultPredefinedColors="predefinedColors.base" + zIndex="5" + ></MultipleColorPicker> + + <ShortTextInput + name="Zadavatel a zpracovatel" + v-model="contractedBy" + :defaultValue="DEFAULT_CONTRACTOR" + :important="false" + zIndex="4" + /> + </template> + </MainContainer> + </main> +</template> + +<style> +@import "vue-select/dist/vue-select.css"; +</style> diff --git a/frontend/src/views/make_a_wish_tour_social_A2/canvas.js b/frontend/src/views/make_a_wish_tour_social_A2/canvas.js new file mode 100644 index 0000000000000000000000000000000000000000..4a43671c7773160aa0ffbd1dfafaa7d7b55b3986 --- /dev/null +++ b/frontend/src/views/make_a_wish_tour_social_A2/canvas.js @@ -0,0 +1,465 @@ +import { fabric } from "fabric"; +import { + clearObjects, + sortObjects, + transformHighlightedText, + checkTextBoxHeight, +} from "../../components/canvas/utils"; +import { PaddedHighlightingTextbox } from "../../components/canvas/textbox"; +import backgroundURL from "../../assets/template/make_a_wish_tour_social_a2/background.png"; +import backgroundURLInverted from "../../assets/template/make_a_wish_banner/background_inverted.png"; +import COLORS from "../../colors"; + +let mainTextBox = null; + +let attendeesTextBox = null; +let dateTextBox = null; +let timeTextBox = null; +let locationTextBox = null; + +let mainImage = null; +let backgroundImage = null; +let previousBackgroundImageColor = null; + +let contractedByTextbox = null; + +let mainImageSource = null; + +const removeDownEventListener = () => { + document + .getElementsByClassName("upper-canvas")[0] + .removeEventListener("pointerdown", canvasPointerDownEvent); +}; + +let upEventFunction = null; +let canvasPointerDownEvent = null; +let pointerDownEventAssigned = false; + +const redraw = async (canvas, options) => { + clearObjects( + [ + mainTextBox, + contractedByTextbox, + attendeesTextBox, + dateTextBox, + timeTextBox, + locationTextBox + ], + canvas, + ); + + document + .getElementsByClassName("upper-canvas")[0] + .removeEventListener("pointerup", upEventFunction); + document + .getElementsByClassName("upper-canvas")[0] + .removeEventListener("pointerout", upEventFunction); + document + .getElementsByClassName("upper-canvas")[0] + .removeEventListener("pointercancel", upEventFunction); + + canvas.preserveObjectStacking = true; + + const textMarginLeft = Math.ceil(canvas.width * 0.1); + const textMarginRight = Math.ceil(canvas.width * 0.078); + + let mainTextMarginBottom = Math.ceil(canvas.height * 0.3); + const dateTextMarginBottom = Math.ceil(canvas.height * 0.605); + const timeTextBoxMarginBottom = Math.ceil(canvas.height * 0.535); + const locationTextBoxMarginBottom = Math.ceil(canvas.height * 0.27); + const attendeesTextBoxMarginBottom = Math.ceil(canvas.height * 0.09); + + const mainTextSize = Math.ceil(canvas.height * 0.175); + const mainTextLineHeight = 1; + + const dateTextSize = Math.ceil(canvas.height * 0.046); + const timeTextSize = Math.ceil(canvas.height * 0.04); + const attendeesTextSize = Math.ceil(canvas.height * 0.027); + const attendeesTextLineHeight = 1; + + const locationTextSize = Math.ceil(canvas.height * 0.044); + const locationTextLineHeight = 1; + + const contractedByTextSize = Math.ceil(canvas.height * 0.015); + const contractedByTextMaxWidth = Math.ceil(canvas.width * 0.9); + const contractedByTextSidesMargin = Math.ceil(canvas.width * 0.03); + + if (options.dateText !== null) { + /* BEGIN Date text render */ + + const dateTextWidth = canvas.width * 5; // IDFK + + console.log(options); + + const highlightedData = transformHighlightedText( + options.dateText, + mainTextSize, + dateTextWidth, + "Bebas Neue", + options.colors.highlightedText.value, + options.colors.highlight.value, + { padWhenDiacritics: false, invertHighlight: true }, + ); + + dateTextBox = new PaddedHighlightingTextbox(highlightedData.text, { + width: dateTextWidth, + left: 0, + textAlign: "right", + fontFamily: "Bebas Neue", + fontSize: dateTextSize, + fill: options.colors.highlight.value, + styles: highlightedData.styles, + selectable: false, + highlightPadding: canvas.height * 0.003, + zIndex: 20, + }); + + checkTextBoxHeight(dateTextBox, 1); + + canvas.add(dateTextBox); + + const dateTextBoxTop = + canvas.height - dateTextBox.height - dateTextMarginBottom; + + dateTextBox.top = dateTextBoxTop - highlightedData.paddingBottom; + + const dateTextBoxLeft = + canvas.width - dateTextBox.width - textMarginRight; + + dateTextBox.left = dateTextBoxLeft; + + canvas.renderAll(); + + /* END Date text render */ + } + + if (options.timeText !== null) { + /* BEGIN Time text render */ + + const timeTextWidth = canvas.width; + + const highlightedData = transformHighlightedText( + options.timeText, + timeTextSize, + timeTextWidth, + "Bebas Neue", + options.colors.highlightedText.value, + options.colors.highlight.value, + { padWhenDiacritics: false, invertHighlight: true }, + ); + + timeTextBox = new PaddedHighlightingTextbox(highlightedData.text, { + width: canvas.width, + left: 0, + textAlign: "right", + fontFamily: "Bebas Neue", + fontSize: timeTextSize, + fill: options.colors.baseText.value, + styles: highlightedData.styles, + selectable: false, + highlightPadding: canvas.height * 0.003, + zIndex: 20, + }); + + checkTextBoxHeight(timeTextBox, 1); + + canvas.add(timeTextBox); + + const timeTextBoxTop = + canvas.height - timeTextBox.height - timeTextBoxMarginBottom; + + timeTextBox.top = timeTextBoxTop - highlightedData.paddingBottom; + + const timeTextBoxLeft = + canvas.width - timeTextBox.width - textMarginRight; + + timeTextBox.left = timeTextBoxLeft; + + canvas.renderAll(); + + /* END Time text render */ + } + + if (options.locationText !== null) { + /* BEGIN Location text render */ + + const locationTextWidth = canvas.width - textMarginLeft - textMarginRight; + + const highlightedData = transformHighlightedText( + options.locationText, + locationTextSize, + locationTextWidth, + "Bebas Neue", + options.colors.highlightedText.value, + options.colors.highlight.value, + { padWhenDiacritics: false, invertHighlight: true }, + ); + + locationTextBox = new PaddedHighlightingTextbox(highlightedData.text, { + width: canvas.width, + left: 0, + textAlign: "right", + fontFamily: "Bebas Neue", + fontSize: locationTextSize, + fill: options.colors.baseText.value, + styles: highlightedData.styles, + lineHeight: locationTextLineHeight, + selectable: false, + highlightPadding: canvas.height * 0.003, + zIndex: 20, + }); + + checkTextBoxHeight(locationTextBox, 2); + + canvas.add(locationTextBox); + + canvas.renderAll(); + + const locationTextBoxTop = + canvas.height - locationTextBox.height - locationTextBoxMarginBottom + (locationTextSize * locationTextLineHeight * (locationTextBox.textLines.length - 1)); + + locationTextBox.top = locationTextBoxTop - highlightedData.paddingBottom; + + const locationTextBoxLeft = + canvas.width - locationTextBox.width - textMarginRight; + + locationTextBox.left = locationTextBoxLeft; + + canvas.renderAll(); + + /* END Location text render */ + } + + if (options.attendeesText !== null) { + /* BEGIN Attendees text render */ + + const attendeesTextWidth = canvas.width - textMarginLeft - textMarginRight; + + const highlightedData = transformHighlightedText( + options.attendeesText, + attendeesTextSize, + attendeesTextWidth, + "Roboto Condensed", + options.colors.highlightedText.value, + options.colors.highlight.value, + { padWhenDiacritics: false, invertHighlight: true }, + ); + + attendeesTextBox = new PaddedHighlightingTextbox(highlightedData.text, { + width: canvas.width, + left: 0, + textAlign: "right", + fontFamily: "Roboto Condensed", + fontSize: attendeesTextSize, + fill: options.colors.baseText.value, + styles: highlightedData.styles, + selectable: false, + highlightPadding: canvas.height * 0.003, + zIndex: 20, + }); + + checkTextBoxHeight(attendeesTextBox, 3); + + canvas.add(attendeesTextBox); + + const attendeesTextBoxTop = + canvas.height - attendeesTextBox.height - attendeesTextBoxMarginBottom + (attendeesTextSize * attendeesTextLineHeight * (attendeesTextBox.textLines.length - 1)); + + attendeesTextBox.top = attendeesTextBoxTop - highlightedData.paddingBottom; + + const attendeesTextBoxLeft = + canvas.width - attendeesTextBox.width - textMarginRight; + + attendeesTextBox.left = attendeesTextBoxLeft; + + canvas.renderAll(); + + /* END Attendees text render */ + } + + if (options.mainText !== null) { + /* BEGIN Background render */ + + if (backgroundImage === null || options.colors.background.value != previousBackgroundImageColor.value || !canvas.getObjects().includes(backgroundImage)) { + backgroundImage = new Image(); + + await new Promise((resolve) => { + backgroundImage.onload = () => { + resolve(); + }; + + if (options.colors.background.value == COLORS.black.value) { + backgroundImage.src = backgroundURL; + } else { + backgroundImage.src = backgroundURLInverted; + } + }); + + backgroundImage = new fabric.Image(backgroundImage, { + top: 0, + left: -50, + zIndex: 10, + selectable: false, + }); + backgroundImage.scaleToHeight(canvas.height); + + previousBackgroundImageColor = options.colors.background; + + canvas.add(backgroundImage); + } + + /* END Background render */ + + + /* BEGIN Main text render */ + + const mainTextWidth = canvas.width - textMarginLeft - textMarginRight; + + const highlightedData = transformHighlightedText( + options.mainText, + mainTextSize, + mainTextWidth, + "Bebas Neue", + options.colors.highlightedText.value, + options.colors.highlight.value, + { padWhenDiacritics: false, invertHighlight: true }, + ); + + mainTextBox = new PaddedHighlightingTextbox(highlightedData.text, { + width: canvas.width, + left: 0, + textAlign: "right", + fontFamily: "Bebas Neue", + fontSize: mainTextSize, + lineHeight: mainTextLineHeight, + fill: options.colors.highlight.value, + styles: highlightedData.styles, + selectable: false, + highlightPadding: canvas.height * 0.003, + zIndex: 20, + }); + + checkTextBoxHeight(mainTextBox, 1); + + canvas.add(mainTextBox); + + const mainTextBoxTop = + canvas.height - mainTextBox.height - mainTextMarginBottom; + + mainTextBox.top = mainTextBoxTop - highlightedData.paddingBottom; + + const mainTextBoxLeft = + canvas.width - mainTextBox.width - textMarginRight; + + mainTextBox.left = mainTextBoxLeft; + + canvas.renderAll(); + + /* END Main text render */ + } + + /* BEGIN Contracted by render */ + + if (options.contractedBy !== null) { + contractedByTextbox = new fabric.Textbox(options.contractedBy, { + left: contractedByTextSidesMargin, + top: canvas.height - contractedByTextSidesMargin - contractedByTextSize, + width: contractedByTextMaxWidth, + fontFamily: "Roboto Condensed", + fontSize: contractedByTextSize, + textAlign: "left", + fill: options.colors.contractedByText.value, + selectable: false, + zIndex: 20, + }); + + checkTextBoxHeight(contractedByTextbox, 1); + + canvas.add(contractedByTextbox); + } + + /* END Contracted by render */ + + /* BEGIN Main image render */ + + if ( + options.mainImage !== null && + (!canvas.contains(mainImage) || + mainImage === null || + options.mainImage.src !== mainImageSource) + ) { + if (mainImage !== null) { + canvas.remove(mainImage); + } + + mainImage = new fabric.Image(options.mainImage, { + left: 0, + top: 0, + zIndex: 0, + }); + + mainImage.controls = { + ...fabric.Image.prototype.controls, + mtr: new fabric.Control({ visible: false }), + }; + + if (mainImage.width >= mainImage.height) { + mainImage.scaleToHeight(canvas.height); + } else { + mainImage.scaleToWidth(canvas.width); + } + + canvas.add(mainImage); + mainImageSource = options.mainImage.src; + // canvas.centerObject(mainImage) + } else if (mainImage !== null && options.mainImage === null) { + canvas.remove(mainImage); + } + + /* END Main image render */ + + sortObjects(canvas); + + canvasPointerDownEvent = (event) => { + let activeObject = canvas.getActiveObject(); + + if (activeObject === null) { + return; + } + + // if (activeObject._element.src == mainImage._element.src) { + // return + // } + + canvas.remove(backgroundImage); + backgroundImage = null; + }; + + if (!pointerDownEventAssigned) { + document + .getElementsByClassName("upper-canvas")[0] + .addEventListener("pointerdown", canvasPointerDownEvent); + + pointerDownEventAssigned = true; + } + + const colors = {...options.colors}; + + upEventFunction = (event) => { + redraw(canvas, {...options, colors: colors}); + }; + + document + .getElementsByClassName("upper-canvas")[0] + .addEventListener("pointerup", upEventFunction); + + document + .getElementsByClassName("upper-canvas")[0] + .addEventListener("pointerout", upEventFunction); + + document + .getElementsByClassName("upper-canvas")[0] + .addEventListener("pointercancel", upEventFunction); +}; + +export default redraw;