Skip to content

Commit

Permalink
Saving pos and size in converter from global to free text block
Browse files Browse the repository at this point in the history
  • Loading branch information
RobolabGs2 committed Jan 15, 2024
1 parent 961ec99 commit b9a3c2a
Show file tree
Hide file tree
Showing 6 changed files with 102 additions and 51 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@

- Клонирование блоков по кнопке в списке блоков.
- Теперь в списках происходит скролл к активному элементу при его смене.
- При преобразовании блока из фиксированного в свободный селектором в списке блоков, его позиция и размеры сохраняются. Добавлена дополнительная опция преобразования со сбросом, работающая как раньше.

### Изменено

Expand Down
1 change: 1 addition & 0 deletions src/lib/App.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,7 @@
effectsShaders={shaders.effects}
version={import.meta.env.VITE_APP_VERSION}
{memeExampleURL}
frameDrawer={memaker?.drawer}
bind:meme={$meme}
bind:frame={$activeFrame}
bind:block={$activeBlock}
Expand Down
55 changes: 37 additions & 18 deletions src/lib/BlockConverter.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -3,46 +3,53 @@
IconBoxAlignBottom,
IconBoxAlignTop,
IconRectangle,
IconSettings
IconSettings,
IconBoxMargin
} from '@tabler/icons-svelte';
import type { Container, Frame } from './meme';
import type { Block, Frame, FrameDrawer } from './meme';
import Select from '$lib/base/Select.svelte';
import type { TextStyle } from './text/text';
export let container: Container;
export let style: TextStyle;
export let block: Block;
export let frame: Frame;
export let iconSize = 16;
export let frameDrawer: FrameDrawer;
function onChange(ev: CustomEvent<{ value: string }>) {
switch (ev.detail.value) {
case 'Top': {
const oldType: string = container.type;
const oldType: string = block.container.type;
if (oldType !== 'global') {
container.value = {
block.container.value = {
maxWidth: 0.95,
maxHeight: 0.4,
minHeight: 0.1
};
}
container.type = 'global';
block.container.type = 'global';
style.baseline = 'top';
break;
}
case 'Bottom': {
const oldType: string = container.type;
const oldType: string = block.container.type;
if (oldType !== 'global') {
container.value = {
block.container.value = {
maxWidth: 0.95,
maxHeight: 0.4,
minHeight: 0.1
};
}
container.type = 'global';
block.container.type = 'global';
style.baseline = 'bottom';
break;
}
case 'Free': {
container.value = {
block.container.value = frameDrawer.measureBlock(frame, block);
block.container.type = 'rectangle';
break;
}
case 'Reset': {
block.container.value = {
width: frame.width / 2,
height: frame.height / 2,
position: {
Expand All @@ -51,7 +58,7 @@
},
rotation: 0
};
container.type = 'rectangle';
block.container.type = 'rectangle';
style.baseline = 'middle';
break;
}
Expand All @@ -61,24 +68,36 @@

<Select
css={{ height: `${iconSize + 16}px`, width: '100%', main: 'flex:1;' }}
items={['Top', 'Bottom', 'Free', 'Custom']}
items={['Top', 'Bottom', 'Free', 'Reset', 'Custom']}
placeholder={'Custom'}
value={container.type == 'rectangle' && style.baseline == 'middle'
value={block.container.type == 'rectangle' && style.baseline == 'middle'
? 'Reset'
: block.container.type == 'rectangle'
? 'Free'
: container.type == 'global' && style.baseline == 'top'
: block.container.type == 'global' && style.baseline == 'top'
? 'Top'
: container.type == 'global' && style.baseline == 'bottom'
: block.container.type == 'global' && style.baseline == 'bottom'
? 'Bottom'
: 'Custom'}
let:item
on:change={onChange}
>
{#if item == 'Top'}
<IconBoxAlignTop size={iconSize} />
<div title="Сверху">
<IconBoxAlignTop size={iconSize} />
</div>
{:else if item == 'Bottom'}
<IconBoxAlignBottom size={iconSize} />
<div title="Снизу">
<IconBoxAlignBottom size={iconSize} />
</div>
{:else if item == 'Free'}
<IconRectangle size={iconSize} />
<div title="Свободный блок">
<IconRectangle size={iconSize} />
</div>
{:else if item == 'Reset'}
<div title="Свободный блок со сбросом размеров">
<IconBoxMargin size={iconSize} />
</div>
{:else if item == 'Custom'}
<IconSettings size={iconSize} />
{:else}
Expand Down
6 changes: 4 additions & 2 deletions src/lib/MemeEditor.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@
IconDeviceFloppy
} from '@tabler/icons-svelte';
import FileInput from './base/FileInput.svelte';
import type { Block, Frame, Meme } from './meme';
import type { Block, Frame, FrameDrawer, Meme } from './meme';
import { createEventDispatcher } from 'svelte';
import { onMount } from 'svelte';
import BlockConverter from './BlockConverter.svelte';
Expand All @@ -68,6 +68,7 @@
export let memeExampleURL: string;
export let effectsShaders: Record<string, RawShader>;
export let editorState: BlockEditorState;
export let frameDrawer: FrameDrawer;
const dispatch = createEventDispatcher<EventsMap>();
Expand Down Expand Up @@ -247,7 +248,8 @@
<BlockConverter
{frame}
bind:style={block.content.value.style}
bind:container={block.container}
bind:block
{frameDrawer}
iconSize={20}
/>
</PreviewActions>
Expand Down
2 changes: 1 addition & 1 deletion src/lib/memaker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ export function isSkinsMap(raw: Record<string, Record<string, FileImport[]>>): r

export class Memaker {
private textures: TextureManager<TextureMeta>;
private drawer: FrameDrawer;
public readonly drawer: FrameDrawer;
public meme: Meme = { frames: [] };
public activeFrame!: Frame;
public activeBlock!: Block;
Expand Down
88 changes: 58 additions & 30 deletions src/lib/meme.ts
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,13 @@ export class FrameDrawer {
if (block.effects.length === 0) return;
graphics.drawModifications(block.effects, rect, buf, graphics.canvasRenderBuffer, true);
}
measureBlock(frame: Frame, block: Block): Rectangle {
const { container, content } = block;
const renderer = this.contentRenderers[content.type];
return container.type === 'global'
? renderer.measureGlobalRectangle(content.value, frame, container.value)
: container.value;
}
}

interface ContentRenderer<T> {
Expand All @@ -116,28 +123,12 @@ interface ContentRenderer<T> {
frame: Frame,
global: GlobalContainer
): Rectangle;
measureGlobalRectangle(content: T, frame: Frame, global: GlobalContainer): Rectangle;
}

class TextContentRenderer implements ContentRenderer<TextContent> {
constructor(private textService: TextStencilService) {}
drawInRectangle(
graphics: Graphics,
destination: TargetFrameBuffer,
content: TextContent,
rect: Rectangle
): Rectangle {
const { text, style } = content;
const textStencil = this.textService.getTextStencil(text, style, rect.width, rect.height);
this.draw(graphics, destination, textStencil, rect, style);
return rect;
}
drawGlobal(
graphics: Graphics,
destination: TargetFrameBuffer,
content: TextContent,
frame: Frame,
global: GlobalContainer
): Rectangle {
private prepareRectangle(content: TextContent, frame: Frame<Block>, global: GlobalContainer) {
const { text, style } = content;
const lines = text.split('\n');
const linesCount = lines.length;
Expand All @@ -162,12 +153,43 @@ class TextContentRenderer implements ContentRenderer<TextContent> {
: baseline == 'middle'
? frame.height / 2
: frame.height - verticalShift;
const rect = {
width,
height,
rotation: 0,
position: { x: frame.width / 2, y }
return {
textStencil,
rect: {
width,
height,
rotation: 0,
position: { x: frame.width / 2, y }
}
};
}
measureGlobalRectangle(
content: TextContent,
frame: Frame<Block>,
global: GlobalContainer
): Rectangle {
return this.prepareRectangle(content, frame, global).rect;
}
drawInRectangle(
graphics: Graphics,
destination: TargetFrameBuffer,
content: TextContent,
rect: Rectangle
): Rectangle {
const { text, style } = content;
const textStencil = this.textService.getTextStencil(text, style, rect.width, rect.height);
this.draw(graphics, destination, textStencil, rect, style);
return rect;
}
drawGlobal(
graphics: Graphics,
destination: TargetFrameBuffer,
content: TextContent,
frame: Frame,
global: GlobalContainer
): Rectangle {
const { style } = content;
const { textStencil, rect } = this.prepareRectangle(content, frame, global);
this.draw(graphics, destination, textStencil, rect, style);
return rect;
}
Expand All @@ -190,6 +212,18 @@ class TextContentRenderer implements ContentRenderer<TextContent> {

class ImageContentRenderer implements ContentRenderer<ImageContent> {
constructor(readonly textures: TextureManager) {}
measureGlobalRectangle(
_content: ImageContent,
frame: Frame<Block>,
global: GlobalContainer
): Rectangle {
return {
width: frame.width * global.maxWidth,
height: frame.height * global.maxHeight,
position: { x: frame.width / 2, y: frame.height / 2 },
rotation: 0
};
}
drawInRectangle(
graphics: Graphics,
destination: TargetFrameBuffer,
Expand All @@ -208,13 +242,7 @@ class ImageContentRenderer implements ContentRenderer<ImageContent> {
frame: Frame,
global: GlobalContainer
): Rectangle {
const size = graphics.size;
const rect = {
width: frame.width * global.maxWidth,
height: frame.height * global.maxHeight,
position: { x: size.width / 2, y: size.height / 2 },
rotation: 0
};
const rect = this.measureGlobalRectangle(content, frame, global);
return this.drawInRectangle(graphics, destination, content, rect);
}
private cropImage(
Expand Down

0 comments on commit b9a3c2a

Please sign in to comment.