Skip to content
Snippets Groups Projects
Commit 83b53e7a authored by Alexa Valentová's avatar Alexa Valentová
Browse files

add new template

parent 1778c6c7
Branches
No related tags found
No related merge requests found
Pipeline #21286 passed
2.23.0 2.24.0
\ No newline at end of file
frontend/src/assets/previews/nakopneme_basic_banner_text_important.png

18.4 KiB

...@@ -37,7 +37,7 @@ export default { ...@@ -37,7 +37,7 @@ export default {
this.redrawing = false; this.redrawing = false;
}, },
downloadImage() { downloadImage() {
this.canvas.discardActiveObject() this.canvas.discardActiveObject();
this.canvas.renderAll(); this.canvas.renderAll();
let link = document.createElement("a"); let link = document.createElement("a");
......
...@@ -268,11 +268,7 @@ const transformHighlightedText = ( ...@@ -268,11 +268,7 @@ const transformHighlightedText = (
line += ""; line += "";
} }
text = setCharAt( text = setCharAt(text, positionWithinString, "");
text,
positionWithinString,
"",
);
} else if (highlightIsActive) { } else if (highlightIsActive) {
highlightedText += character; highlightedText += character;
...@@ -355,11 +351,10 @@ const transformHighlightedText = ( ...@@ -355,11 +351,10 @@ const transformHighlightedText = (
: 0.1), : 0.1),
) )
: 0, : 0,
rects: rects rects: rects,
}; };
}; };
const checkTextBoxHeight = (textBox, maxLines) => { const checkTextBoxHeight = (textBox, maxLines) => {
if (textBox.textLines.length > maxLines) { if (textBox.textLines.length > maxLines) {
if (!window.showingMaxLinesWarning) { if (!window.showingMaxLinesWarning) {
......
...@@ -6,7 +6,14 @@ import { sanitizeValue } from "./utils"; ...@@ -6,7 +6,14 @@ import { sanitizeValue } from "./utils";
<script> <script>
export default { export default {
components: { InputHeading }, components: { InputHeading },
props: ["name", "important", "highlightable", "zIndex", "customHighlightText", "modelValue"], props: [
"name",
"important",
"highlightable",
"zIndex",
"customHighlightText",
"modelValue",
],
emits: ["update:modelValue"], emits: ["update:modelValue"],
}; };
</script> </script>
......
...@@ -2,6 +2,7 @@ import basicPhotoBannerImage from "./assets/previews/basic_photo_banner.png"; ...@@ -2,6 +2,7 @@ import basicPhotoBannerImage from "./assets/previews/basic_photo_banner.png";
import nakopnemeBasicPhotoBannerImage from "./assets/previews/nakopneme_basic_photo_banner.png"; import nakopnemeBasicPhotoBannerImage from "./assets/previews/nakopneme_basic_photo_banner.png";
import nakopnemeBasicPhotoBannerImageFZ from "./assets/previews/nakopneme_basic_photo_banner_fz.png"; import nakopnemeBasicPhotoBannerImageFZ from "./assets/previews/nakopneme_basic_photo_banner_fz.png";
import nakopnemeBasicPhotoBannerImageFZBar from "./assets/previews/nakopneme_basic_photo_banner_fzbar.png"; import nakopnemeBasicPhotoBannerImageFZBar from "./assets/previews/nakopneme_basic_photo_banner_fzbar.png";
import nakopnemeBasicPhotoBannerTextImportant from "./assets/previews/nakopneme_basic_banner_text_important.png";
import urgentBasicPhotoBannerImage from "./assets/previews/urgent_basic_photo_banner.png"; import urgentBasicPhotoBannerImage from "./assets/previews/urgent_basic_photo_banner.png";
import makeawishPhotoBannerImage from "./assets/previews/make_a_wish_photo_banner.png"; import makeawishPhotoBannerImage from "./assets/previews/make_a_wish_photo_banner.png";
import makeawishTourSocialImage from "./assets/previews/make_a_wish_tour_social.png"; import makeawishTourSocialImage from "./assets/previews/make_a_wish_tour_social.png";
...@@ -96,6 +97,18 @@ const TEMPLATES = { ...@@ -96,6 +97,18 @@ const TEMPLATES = {
title: "Nakopneme to! Základní banner s fotkou (F/Ž zvýraznění)", title: "Nakopneme to! Základní banner s fotkou (F/Ž zvýraznění)",
}, },
}, },
nakopneme_basic_banner_text_important: {
name: "Nakopneme to! - Text s vykřičníkem a textem",
image: nakopnemeBasicPhotoBannerTextImportant,
path: "/nakopneme-basic-banner-text-important",
component: () =>
import(
"./views/nakopneme_basic_banner_text_important/NakopnemeBasicBannerTextImportant.vue"
),
meta: {
title: "Nakopneme to! - Text s vykřičníkem a textem",
},
},
make_a_wish_banner: { make_a_wish_banner: {
name: "Máte přání banner", name: "Máte přání banner",
image: makeawishPhotoBannerImage, image: makeawishPhotoBannerImage,
......
<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 {
generateDefaultBadges,
LOGO_POSITIONS,
generateLogoPositions,
} from "../../logos";
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,
LongTextInput,
ShortTextInput,
RangeInput,
SelectInput,
InputSeparator,
ImageInput,
MultipleColorPicker,
},
data() {
const predefinedColors = {
base: {
name: "Základní barvy",
colors: {
background: COLORS.black,
highlight: COLORS.yellow1,
baseText: COLORS.black,
highlightedText: COLORS.black,
contractedByText: COLORS.gray1,
},
},
};
return {
mainText: null,
contractedBy: DEFAULT_CONTRACTOR,
logoImage: null,
logoPosition: LOGO_POSITIONS.top_right,
logoOptions: generateLogoPositions(["top_right", "top_left"]),
colorLabels: {
background: "Pozadí",
highlight: "Zvýraznění",
baseText: "Text",
highlightedText: "Zvýrazněný text",
},
predefinedColors: predefinedColors,
colors: predefinedColors.base.colors,
predefinedBadgeImages: generateDefaultBadges("defaultDark"),
autoRedraw: false,
};
},
async created() {
await loadCanvasStorage(this);
},
methods: {
async reloadCanvasProperties() {
const canvasProperties = {
mainText: this.mainText,
logoPosition: this.logoPosition,
contractedBy: this.contractedBy,
logoImage: this.logoImage,
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.mainText,
vm.logoPosition,
vm.contractedBy,
vm.logoImage,
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="1080"
height="1350"
/>
</template>
<template v-slot:right>
<ReloadButton :parentRefs="$refs" @click="reloadCanvasProperties" />
<AutoReloadCheckbox v-model="autoRedraw" />
<LongTextInput
name="Hlavní text"
v-model="mainText"
:important="true"
:highlightable="true"
customHighlightText="V této šabloně lze zvýrazňovat pouze celé řádky, pro správnou funkci zvýrazněte pouze celé řádky."
zIndex="9"
/>
<InputSeparator />
<ImageInput
name="Obrázek loga"
v-model="logoImage"
:important="false"
:predefinedImages="predefinedBadgeImages"
:mustSelectPredefinedImage="true"
:disableImageInput="true"
zIndex="7"
/>
<SelectInput
name="Pozice loga"
:options="logoOptions"
v-model="logoPosition"
zIndex="6"
/>
<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>
import * as fabric from "fabric";
import {
clearObjects,
sortObjects,
transformHighlightedText,
checkTextBoxHeight,
} from "../../components/canvas/utils";
import { PaddedHighlightingTextbox } from "../../components/canvas/textbox";
let mainTextBox = null;
let exclTextBox = null;
let background = null;
let logoImage = null;
let contractedByTextbox = null;
let previousLogoPosition = null;
const redraw = async (canvas, options) => {
canvas.controlsAboveOverlay = true;
clearObjects(
[
mainTextBox,
contractedByTextbox,
],
canvas,
);
const textMarginLeft = Math.ceil(canvas.width * 0.14);
const textMarginRight = Math.ceil(canvas.width * 0.075);
let mainTextMarginBottom = Math.ceil(canvas.height * 0.42);
const mainTextBackgroundMarginTop = Math.ceil(canvas.height * 0.1);
const mainTextSize = Math.ceil(canvas.height * 0.085);
const mainTextHeightLimit = Math.ceil(mainTextSize * 3.3);
const mainTextLineHeight = 0.9;
const contractedByTextSize = Math.ceil(canvas.height * 0.02);
const contractedByTextMaxWidth = Math.ceil(canvas.width * 0.9);
const contractedByTextSidesMargin = Math.ceil(canvas.width * 0.03);
const logoWidth = Math.ceil(canvas.width * 0.13);
const logoSideMargin = Math.ceil(canvas.width * 0.07);
if (!background) {
background = new fabric.Rect({
width: canvas.width,
height: canvas.height,
selectable: false,
zIndex: 1,
fill: new fabric.Gradient({
type: "linear",
gradientUnits: "pixels",
coords: {
x1: 0,
y1: 0,
x2: 0,
y2: canvas.height
},
colorStops: [
{
offset: 1,
color: "#FDC801",
},
{
offset: 0,
color: "#CF7BCC",
},
],
})
})
canvas.add(background)
}
if (options.mainText !== null) {
/* BEGIN Main text render */
const mainTextWidth = canvas.width - textMarginLeft - textMarginRight;
const highlightedData = transformHighlightedText(
options.mainText,
mainTextSize,
mainTextWidth,
"Bebas Neue",
options.colors.highlight.value,
options.colors.highlightedText.value,
{ padWhenDiacritics: true, skipWhitespaceNormalization: true },
);
mainTextBox = new PaddedHighlightingTextbox(highlightedData.text, {
width: canvas.width,
left: 0,
textAlign: "center",
fontStyle: "italic",
fontFamily: "Bebas Neue",
fontSize: mainTextSize,
lineHeight: mainTextLineHeight,
fill: options.colors.baseText.value,
styles: highlightedData.styles,
selectable: false,
highlightPadding: canvas.height * 0.003,
zIndex: 10,
});
checkTextBoxHeight(mainTextBox, 4);
canvas.add(mainTextBox);
const mainTextBoxTop =
canvas.height - mainTextMarginBottom;
mainTextBox.top = mainTextBoxTop - highlightedData.paddingBottom;
canvas.renderAll();
/* END Main text render */
/* BEGIN Excl. mark render */
exclTextBox = new fabric.Textbox(
"!",
{
width: canvas.width,
height: canvas.height,
fill: options.colors.baseText.value,
selectable: false,
zIndex: 10,
textAlign: "center",
fontFamily: "Bebas Neue",
fontSize: 700,
top: 100,
left: 0,
}
)
canvas.add(exclTextBox);
/* END Excl. mark render */
}
/* BEGIN Logo render */
// A logo is provided, and it either hasn't been rendered yet or is a new one.
const createNewLogo =
(options.logoImage !== null &&
(logoImage === null ||
(options.logoImage !== null &&
options.logoImage !== logoImage._element))) ||
previousLogoPosition != options.logoPosition.id;
previousLogoPosition = options.logoPosition.id;
if (createNewLogo) {
canvas.remove(logoImage);
logoImage = new fabric.Image(options.logoImage, { selectable: false });
logoImage.scaleToWidth(logoWidth);
if (options.logoPosition.id == "top-right") {
logoImage.set({
left: canvas.width - logoWidth - logoSideMargin,
top: logoSideMargin,
zIndex: 11,
});
} else {
logoImage.set({
left: logoSideMargin,
top: logoSideMargin,
zIndex: 11,
});
}
canvas.add(logoImage);
}
/* END Logo render */
/* BEGIN Contracted by render */
if (options.contractedBy !== null) {
contractedByTextbox = new fabric.Textbox(options.contractedBy, {
left:
canvas.width - contractedByTextMaxWidth - contractedByTextSidesMargin,
top: canvas.height - contractedByTextSidesMargin - contractedByTextSize,
width: contractedByTextMaxWidth,
fontFamily: "Roboto Condensed",
fontSize: contractedByTextSize,
textAlign: "right",
fill: "#000",
selectable: false,
zIndex: 10,
});
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.setControlsVisibility({
// corners (uniform scale)
tl: true,
tr: true,
bl: true,
br: true,
// mids (scale X/Y independently)
ml: true,
mr: true,
mt: true,
mb: true,
// rotation
mtr: 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);
};
export default redraw;
...@@ -81,11 +81,14 @@ const redraw = async (canvas, options) => { ...@@ -81,11 +81,14 @@ const redraw = async (canvas, options) => {
/* BEGIN Main text render */ /* BEGIN Main text render */
const mainText = options.mainText; const mainText = options.mainText;
const mainTextWidth = canvas.width - (textMarginRight * 2); const mainTextWidth = canvas.width - textMarginRight * 2;
// Estimate where the Textbox will sit // Estimate where the Textbox will sit
const estimatedLines = Math.ceil(mainText.length / (mainTextWidth / mainTextSize)); const estimatedLines = Math.ceil(
const estimatedTextBoxHeight = estimatedLines * mainTextSize * mainTextLineHeight; mainText.length / (mainTextWidth / mainTextSize),
);
const estimatedTextBoxHeight =
estimatedLines * mainTextSize * mainTextLineHeight;
const mainTextBoxTopEstimate = const mainTextBoxTopEstimate =
canvas.height - estimatedTextBoxHeight - mainTextMarginBottom; canvas.height - estimatedTextBoxHeight - mainTextMarginBottom;
...@@ -106,7 +109,7 @@ const redraw = async (canvas, options) => { ...@@ -106,7 +109,7 @@ const redraw = async (canvas, options) => {
y: mainTextBoxTopEstimate, y: mainTextBoxTopEstimate,
skipAddingBg: true, skipAddingBg: true,
highlightContainedToLine: true, highlightContainedToLine: true,
} },
); );
// Create and measure your Fabric textbox // Create and measure your Fabric textbox
...@@ -127,7 +130,11 @@ const redraw = async (canvas, options) => { ...@@ -127,7 +130,11 @@ const redraw = async (canvas, options) => {
canvas.add(mainTextBox); canvas.add(mainTextBox);
// Now position it using the real paddingBottom // Now position it using the real paddingBottom
mainTextBox.top = canvas.height - mainTextBox.height - highlightedData.paddingBottom - mainTextMarginBottom; mainTextBox.top =
canvas.height -
mainTextBox.height -
highlightedData.paddingBottom -
mainTextMarginBottom;
const mainTextBoxTop = mainTextBox.top; const mainTextBoxTop = mainTextBox.top;
// grab the original so we can still draw the text // grab the original so we can still draw the text
...@@ -153,28 +160,30 @@ const redraw = async (canvas, options) => { ...@@ -153,28 +160,30 @@ const redraw = async (canvas, options) => {
const height = this.fontSize * 1.05; // adjust vertical padding if you like const height = this.fontSize * 1.05; // adjust vertical padding if you like
const defaultGradient = new fabric.Gradient({ const defaultGradient = new fabric.Gradient({
type: 'linear', type: "linear",
gradientUnits: 'pixels', gradientUnits: "pixels",
coords: { x1: 0, y1: 0, x2: lineWidth + 20, y2: 0 }, coords: { x1: 0, y1: 0, x2: lineWidth + 20, y2: 0 },
colorStops: [ colorStops: [
{ offset: 0, color: '#CF7BCC' }, { offset: 0, color: "#CF7BCC" },
{ offset: 1, color: '#FDC801' }, { offset: 1, color: "#FDC801" },
], ],
}); });
// build the rect // build the rect
const rect = new fabric.Rect({ const rect = new fabric.Rect({
left: canvas.width / 2 + left - 20, left: canvas.width / 2 + left - 20,
top: canvas.height - mainTextMarginBottom - highlightedData.paddingBottom * 0.5 - 20 - ( top:
(mainTextBox._textLines.length - lineIndex) canvas.height -
* mainTextSize mainTextMarginBottom -
highlightedData.paddingBottom * 0.5 -
20 -
(mainTextBox._textLines.length - lineIndex) * mainTextSize,
// * mainTextLineHeight // * mainTextLineHeight
),
width: lineWidth, // Add padding to left side, line breaks do right width: lineWidth, // Add padding to left side, line breaks do right
height: height, height: height,
fill: defaultGradient, fill: defaultGradient,
originX: 'left', originX: "left",
originY: 'top', originY: "top",
evented: false, evented: false,
selectable: false, selectable: false,
zIndex: 9, zIndex: 9,
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment