Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make canvas responsive #18

Merged
merged 6 commits into from
Apr 15, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,10 @@ import { Provider } from "react-redux"
function App() {
return (
<Provider store={store}>
<div className="flex flex-row flex-wrap">
<div className="flex flex-row flex-wrap overflow-hidden">
<Toaster />

<aside className="order-2 w-full border-gray-800 sm:order-1 sm:w-3/12 sm:border-r sm:bg-neutral-900">
<aside className="absolute bottom-0 z-10 order-2 w-full border-gray-800 sm:relative sm:order-1 sm:w-3/12 sm:border-r sm:bg-neutral-900">
<EditingPanel />
</aside>

Expand Down
12 changes: 9 additions & 3 deletions src/components/Canvas/Canvas.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,10 @@ export default function Canvas() {
wrapperRef.current.clientWidth > 640
? 640 // fixed 640px canvas on >640px devices
: wrapperRef.current.clientWidth - 16 // 16px margin
const ratio = ASPECT_RATIOS[activeRatioIndex].canvas(panelWidth)
const ratio = ASPECT_RATIOS[activeRatioIndex].getCanvasSize(
panelWidth,
wrapperRef.current.clientHeight - 16,
)

// 1. Setup canvas
const canvas = new fabric.Canvas(canvasRef.current, {
Expand Down Expand Up @@ -167,8 +170,11 @@ export default function Canvas() {
}, [activeRatioIndex, activeTemplateIndex])

return (
<div ref={wrapperRef}>
<div className="flex items-start justify-center py-2 sm:min-h-screen sm:py-8">
<div
ref={wrapperRef}
className="flex h-[calc(100vh-14rem)] items-center justify-center sm:h-[calc(99vh-3rem)]"
>
<div>
<canvas ref={canvasRef} />
</div>
<div className="hidden">
Expand Down
20 changes: 11 additions & 9 deletions src/constants/canvasConfig.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { LockedObjectType, CollageTemplateType, AspectRatioType } from "@/types"
import calculateWidthByAspectRatio from "@/utils/aspectRatioHelper"
import calculateAspectRatio from "@/utils/aspectRatioHelper"

export const OBJECT_LOCKED: LockedObjectType = {
lockMovementX: true,
Expand Down Expand Up @@ -478,49 +478,51 @@ export const ASPECT_RATIOS: AspectRatioType[] = [
{
name: "1:1",
nickname: "Square",
canvas: (FIXED_WIDTH) => calculateWidthByAspectRatio(1, 1, FIXED_WIDTH),
getCanvasSize: (WIDTH, HEIGHT) => calculateAspectRatio(1, 1, WIDTH, HEIGHT),
icon: "data:image/svg+xml,%3Csvg width='30' height='30' viewBox='0 0 30 30' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cg clip-path='url(%23clip0_6_2)'%3E%3Crect x='0.5' y='0.5' width='29' height='29' stroke='white'/%3E%3Cg clip-path='url(%23clip1_6_2)'%3E%3Cpath d='M9.6014 26.9266H2.97553V20.3007L4.15162 20.3007L4.15162 25.7505L9.6014 25.7505V26.9266Z' fill='white'/%3E%3C/g%3E%3Cg clip-path='url(%23clip2_6_2)'%3E%3Cpath d='M20.3007 2.97552H26.9266V9.60139L25.7505 9.60139L25.7505 4.15161L20.3007 4.15161V2.97552Z' fill='white'/%3E%3C/g%3E%3C/g%3E%3Cdefs%3E%3CclipPath id='clip0_6_2'%3E%3Crect width='30' height='30' fill='white'/%3E%3C/clipPath%3E%3CclipPath id='clip1_6_2'%3E%3Crect width='11.2445' height='11.2445' fill='white' transform='translate(-1 22.951) rotate(-45)'/%3E%3C/clipPath%3E%3CclipPath id='clip2_6_2'%3E%3Crect width='11.2445' height='11.2445' fill='white' transform='translate(30.9021 6.95105) rotate(135)'/%3E%3C/clipPath%3E%3C/defs%3E%3C/svg%3E%0A",
},
{
name: "2:1",
nickname: "FB Post",
canvas: (FIXED_WIDTH) => calculateWidthByAspectRatio(2, 1, FIXED_WIDTH),
getCanvasSize: (WIDTH, HEIGHT) => calculateAspectRatio(2, 1, WIDTH, HEIGHT),
icon: "data:image/svg+xml,%3Csvg width='30' height='30' viewBox='0 0 30 30' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cg clip-path='url(%23clip0_6_14)'%3E%3Crect x='0.5' y='7.5' width='29' height='14' stroke='white'/%3E%3Cg clip-path='url(%23clip1_6_14)'%3E%3Cpath d='M8.6014 19.9266H1.97553V13.3007L3.15162 13.3007L3.15162 18.7505L8.6014 18.7505V19.9266Z' fill='white'/%3E%3C/g%3E%3Cg clip-path='url(%23clip2_6_14)'%3E%3Cpath d='M21.3007 8.97552H27.9266V15.6014L26.7505 15.6014L26.7505 10.1516L21.3007 10.1516V8.97552Z' fill='white'/%3E%3C/g%3E%3C/g%3E%3Cdefs%3E%3CclipPath id='clip0_6_14'%3E%3Crect width='30' height='30' fill='white'/%3E%3C/clipPath%3E%3CclipPath id='clip1_6_14'%3E%3Crect width='11.2445' height='11.2445' fill='white' transform='translate(-2 15.951) rotate(-45)'/%3E%3C/clipPath%3E%3CclipPath id='clip2_6_14'%3E%3Crect width='11.2445' height='11.2445' fill='white' transform='translate(31.9021 12.951) rotate(135)'/%3E%3C/clipPath%3E%3C/defs%3E%3C/svg%3E%0A",
},
{
name: "2:3",
nickname: "Pinterest Pin",
canvas: (FIXED_WIDTH) => calculateWidthByAspectRatio(2, 3, FIXED_WIDTH),
getCanvasSize: (WIDTH, HEIGHT) => calculateAspectRatio(2, 3, WIDTH, HEIGHT),
icon: "data:image/svg+xml,%3Csvg width='30' height='30' viewBox='0 0 30 30' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cg clip-path='url(%23clip0_6_21)'%3E%3Crect x='5.5' y='0.5' width='19' height='29' stroke='white'/%3E%3Cg clip-path='url(%23clip1_6_21)'%3E%3Cpath d='M13.6014 27.9266H6.97553V21.3007L8.15162 21.3007L8.15162 26.7505L13.6014 26.7505V27.9266Z' fill='white'/%3E%3C/g%3E%3Cg clip-path='url(%23clip2_6_21)'%3E%3Cpath d='M16.3007 1.97552H22.9266V8.60139L21.7505 8.60139L21.7505 3.15161L16.3007 3.15161V1.97552Z' fill='white'/%3E%3C/g%3E%3C/g%3E%3Cdefs%3E%3CclipPath id='clip0_6_21'%3E%3Crect width='30' height='30' fill='white'/%3E%3C/clipPath%3E%3CclipPath id='clip1_6_21'%3E%3Crect width='11.2445' height='11.2445' fill='white' transform='translate(3 23.951) rotate(-45)'/%3E%3C/clipPath%3E%3CclipPath id='clip2_6_21'%3E%3Crect width='11.2445' height='11.2445' fill='white' transform='translate(26.9021 5.95105) rotate(135)'/%3E%3C/clipPath%3E%3C/defs%3E%3C/svg%3E%0A",
},
{
name: "3:1",
nickname: "Twitter Header",
canvas: (FIXED_WIDTH) => calculateWidthByAspectRatio(3, 1, FIXED_WIDTH),
getCanvasSize: (WIDTH, HEIGHT) => calculateAspectRatio(3, 1, WIDTH, HEIGHT),
icon: "data:image/svg+xml,%3Csvg width='30' height='30' viewBox='0 0 30 30' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cg clip-path='url(%23clip0_6_28)'%3E%3Crect x='0.5' y='10.5' width='29' height='9' stroke='white'/%3E%3Cg clip-path='url(%23clip1_6_28)'%3E%3Cpath d='M6.54244 18.0284H1.8284V13.3144L2.66514 13.3144L2.66514 17.1917L6.54244 17.1917V18.0284Z' fill='white'/%3E%3C/g%3E%3Cg clip-path='url(%23clip2_6_28)'%3E%3Cpath d='M23.4289 11.8284H28.143V16.5425L27.3062 16.5425L27.3062 12.6652L23.4289 12.6652V11.8284Z' fill='white'/%3E%3C/g%3E%3C/g%3E%3Cdefs%3E%3CclipPath id='clip0_6_28'%3E%3Crect width='30' height='30' fill='white'/%3E%3C/clipPath%3E%3CclipPath id='clip1_6_28'%3E%3Crect width='8' height='8' fill='white' transform='translate(-1 15.2) rotate(-45)'/%3E%3C/clipPath%3E%3CclipPath id='clip2_6_28'%3E%3Crect width='8' height='8' fill='white' transform='translate(31.3137 14.6569) rotate(135)'/%3E%3C/clipPath%3E%3C/defs%3E%3C/svg%3E%0A",
},
{
name: "3:4",
nickname: "Twitter Tall Photo",
canvas: (FIXED_WIDTH) => calculateWidthByAspectRatio(3, 4, FIXED_WIDTH),
getCanvasSize: (WIDTH, HEIGHT) => calculateAspectRatio(3, 4, WIDTH, HEIGHT),
icon: "data:image/svg+xml,%3Csvg width='30' height='30' viewBox='0 0 30 30' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cg clip-path='url(%23clip0_7_2)'%3E%3Crect x='4.5' y='0.5' width='21' height='29' stroke='white'/%3E%3Cg clip-path='url(%23clip1_7_2)'%3E%3Cpath d='M12.6014 27.9266H5.97553V21.3007L7.15162 21.3007L7.15162 26.7505L12.6014 26.7505V27.9266Z' fill='white'/%3E%3C/g%3E%3Cg clip-path='url(%23clip2_7_2)'%3E%3Cpath d='M17.3007 1.97552H23.9266V8.60139L22.7505 8.60139L22.7505 3.15161L17.3007 3.15161V1.97552Z' fill='white'/%3E%3C/g%3E%3C/g%3E%3Cdefs%3E%3CclipPath id='clip0_7_2'%3E%3Crect width='30' height='30' fill='white'/%3E%3C/clipPath%3E%3CclipPath id='clip1_7_2'%3E%3Crect width='11.2445' height='11.2445' fill='white' transform='translate(2 23.951) rotate(-45)'/%3E%3C/clipPath%3E%3CclipPath id='clip2_7_2'%3E%3Crect width='11.2445' height='11.2445' fill='white' transform='translate(27.9021 5.95105) rotate(135)'/%3E%3C/clipPath%3E%3C/defs%3E%3C/svg%3E%0A",
},
{
name: "4:3",
nickname: "Standard",
canvas: (FIXED_WIDTH) => calculateWidthByAspectRatio(4, 3, FIXED_WIDTH),
getCanvasSize: (WIDTH, HEIGHT) => calculateAspectRatio(4, 3, WIDTH, HEIGHT),
icon: "data:image/svg+xml,%3Csvg width='30' height='30' viewBox='0 0 30 30' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cg clip-path='url(%23clip0_7_9)'%3E%3Crect x='0.5' y='4.5' width='29' height='22' stroke='white'/%3E%3Cg clip-path='url(%23clip1_7_9)'%3E%3Cpath d='M8.6014 24.9266H1.97553V18.3007L3.15162 18.3007L3.15162 23.7505L8.6014 23.7505V24.9266Z' fill='white'/%3E%3C/g%3E%3Cg clip-path='url(%23clip2_7_9)'%3E%3Cpath d='M21.3007 5.97552H27.9266V12.6014L26.7505 12.6014L26.7505 7.15161L21.3007 7.15161V5.97552Z' fill='white'/%3E%3C/g%3E%3C/g%3E%3Cdefs%3E%3CclipPath id='clip0_7_9'%3E%3Crect width='30' height='30' fill='white'/%3E%3C/clipPath%3E%3CclipPath id='clip1_7_9'%3E%3Crect width='11.2445' height='11.2445' fill='white' transform='translate(-2 20.951) rotate(-45)'/%3E%3C/clipPath%3E%3CclipPath id='clip2_7_9'%3E%3Crect width='11.2445' height='11.2445' fill='white' transform='translate(31.9021 9.95105) rotate(135)'/%3E%3C/clipPath%3E%3C/defs%3E%3C/svg%3E%0A",
},
{
name: "9:16",
nickname: "Stories",
canvas: (FIXED_WIDTH) => calculateWidthByAspectRatio(9, 16, FIXED_WIDTH),
getCanvasSize: (WIDTH, HEIGHT) =>
calculateAspectRatio(9, 16, WIDTH, HEIGHT),
icon: "data:image/svg+xml,%3Csvg width='30' height='30' viewBox='0 0 30 30' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cg clip-path='url(%23clip0_7_23)'%3E%3Crect x='7.5' y='0.5' width='15.8' height='29' stroke='white'/%3E%3Cg clip-path='url(%23clip1_7_23)'%3E%3Cpath d='M15.6014 27.9266H8.97553V21.3007L10.1516 21.3007L10.1516 26.7505L15.6014 26.7505V27.9266Z' fill='white'/%3E%3C/g%3E%3Cg clip-path='url(%23clip2_7_23)'%3E%3Cpath d='M15.3007 1.97555H21.9266V8.60142L20.7505 8.60142L20.7505 3.15164L15.3007 3.15164V1.97555Z' fill='white'/%3E%3C/g%3E%3C/g%3E%3Cdefs%3E%3CclipPath id='clip0_7_23'%3E%3Crect width='30' height='30' fill='white'/%3E%3C/clipPath%3E%3CclipPath id='clip1_7_23'%3E%3Crect width='11.2445' height='11.2445' fill='white' transform='translate(5 23.951) rotate(-45)'/%3E%3C/clipPath%3E%3CclipPath id='clip2_7_23'%3E%3Crect width='11.2445' height='11.2445' fill='white' transform='translate(25.9021 5.95108) rotate(135)'/%3E%3C/clipPath%3E%3C/defs%3E%3C/svg%3E%0A",
},
{
name: "16:9",
nickname: "Cinematic",
canvas: (FIXED_WIDTH) => calculateWidthByAspectRatio(16, 9, FIXED_WIDTH),
getCanvasSize: (WIDTH, HEIGHT) =>
calculateAspectRatio(16, 9, WIDTH, HEIGHT),
icon: "data:image/svg+xml,%3Csvg width='30' height='30' viewBox='0 0 30 30' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cg clip-path='url(%23clip0_7_16)'%3E%3Crect x='0.5' y='7.5' width='29' height='16' stroke='white'/%3E%3Cg clip-path='url(%23clip1_7_16)'%3E%3Cpath d='M8.6014 21.9266H1.97553V15.3007L3.15162 15.3007L3.15162 20.7505L8.6014 20.7505V21.9266Z' fill='white'/%3E%3C/g%3E%3Cg clip-path='url(%23clip2_7_16)'%3E%3Cpath d='M21.3007 8.97552H27.9266V15.6014L26.7505 15.6014L26.7505 10.1516L21.3007 10.1516V8.97552Z' fill='white'/%3E%3C/g%3E%3C/g%3E%3Cdefs%3E%3CclipPath id='clip0_7_16'%3E%3Crect width='30' height='30' fill='white'/%3E%3C/clipPath%3E%3CclipPath id='clip1_7_16'%3E%3Crect width='11.2445' height='11.2445' fill='white' transform='translate(-2 17.951) rotate(-45)'/%3E%3C/clipPath%3E%3CclipPath id='clip2_7_16'%3E%3Crect width='11.2445' height='11.2445' fill='white' transform='translate(31.9021 12.951) rotate(135)'/%3E%3C/clipPath%3E%3C/defs%3E%3C/svg%3E%0A",
},
]
13 changes: 7 additions & 6 deletions src/types/canvas.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,16 +25,17 @@ export type CollageTemplateType = {
config: RectConfigType[]
}

export type DimensionsType = {
width: number
height: number
}

export type AspectRatioType = {
name: string
nickname: string
icon: string
canvas: (w: number) => DimensionsType
getCanvasSize: (
WIDTH: number,
HEIGHT: number,
) => {
width: number
height: number
}
}

export interface CanvasStateType {
Expand Down
24 changes: 16 additions & 8 deletions src/utils/aspectRatioHelper.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,20 @@
import type { DimensionsType } from "@/types"

export default function calculateWidthByAspectRatio(
export default function calculateAspectRatio(
widthRatio: number,
heightRatio: number,
exactWidth: number,
): DimensionsType {
return {
width: exactWidth,
height: Math.round((exactWidth / widthRatio) * heightRatio),
wrapperWidth: number,
wrapperHeight: number,
) {
if (wrapperWidth / wrapperHeight >= widthRatio / heightRatio) {
// Use container height as the reference
return {
height: wrapperHeight,
width: Math.round((wrapperHeight * widthRatio) / heightRatio),
}
} else {
// Use container width as the reference
return {
height: Math.round((wrapperWidth * heightRatio) / widthRatio),
width: wrapperWidth,
}
}
}
Loading