Emoji: stretching from one side does not resize (only shrink works on side handles) #119

Open
opened 2026-04-30 11:13:38 +00:00 by AhmedHanafy725 · 3 comments
Member

Summary

Drag a side handle of an emoji selection to enlarge it (e.g. drag the right edge outward) — nothing happens, the emoji stays the same size. Dragging a side handle inward to shrink works. Corner drags work in both directions.

Steps to reproduce

  1. Drop an emoji on the board, click it.
  2. Drag the right (or left / top / bottom) handle outward to make it bigger.

Expected

Emoji grows (with aspect ratio preserved).

Actual

Emoji size doesn't change.

Root cause

crates/hero_whiteboard_ui/static/web/js/whiteboard/objects.js::applyTransform for type === 'emoji' uses Math.min(scaleX, scaleY) to pick a uniform scale (so the glyph isn't squashed):

var s = Math.min(scaleX, scaleY);
var newFs = Math.max(8, Math.round(baseFs * s));
emojiText.fontSize(newFs);

When the user drags one side handle, the perpendicular axis stays at scale 1. So:

  • Shrink (e.g. scaleX = 0.5, scaleY = 1) → min(0.5, 1) = 0.5 ✓ (emoji shrinks)
  • Stretch (e.g. scaleX = 2.0, scaleY = 1) → min(2.0, 1) = 1.0 ✗ (no change)

The min picks the wrong axis when one side is at 1 and the other has been pulled > 1.

Fix

Pick whichever axis moved further from 1 — i.e. the axis the user actually dragged:

var dx = Math.abs(scaleX - 1);
var dy = Math.abs(scaleY - 1);
var s = dx >= dy ? scaleX : scaleY;

Both side and corner drags now resize correctly in both directions, while still applying a single uniform scale so the emoji glyph isn't squashed.

## Summary Drag a side handle of an emoji selection to enlarge it (e.g. drag the right edge outward) — nothing happens, the emoji stays the same size. Dragging a side handle inward to shrink works. Corner drags work in both directions. ## Steps to reproduce 1. Drop an emoji on the board, click it. 2. Drag the right (or left / top / bottom) handle outward to make it bigger. ## Expected Emoji grows (with aspect ratio preserved). ## Actual Emoji size doesn't change. ## Root cause `crates/hero_whiteboard_ui/static/web/js/whiteboard/objects.js::applyTransform` for `type === 'emoji'` uses `Math.min(scaleX, scaleY)` to pick a uniform scale (so the glyph isn't squashed): ```js var s = Math.min(scaleX, scaleY); var newFs = Math.max(8, Math.round(baseFs * s)); emojiText.fontSize(newFs); ``` When the user drags **one** side handle, the perpendicular axis stays at scale 1. So: - Shrink (e.g. `scaleX = 0.5, scaleY = 1`) → `min(0.5, 1) = 0.5` ✓ (emoji shrinks) - Stretch (e.g. `scaleX = 2.0, scaleY = 1`) → `min(2.0, 1) = 1.0` ✗ (no change) The `min` picks the wrong axis when one side is at 1 and the other has been pulled > 1. ## Fix Pick whichever axis moved further from 1 — i.e. the axis the user actually dragged: ```js var dx = Math.abs(scaleX - 1); var dy = Math.abs(scaleY - 1); var s = dx >= dy ? scaleX : scaleY; ``` Both side and corner drags now resize correctly in both directions, while still applying a single uniform scale so the emoji glyph isn't squashed.
Author
Member

Implementation Spec for Issue #119

Objective

Side-handle resize of an emoji must work in both directions (stretch and shrink), with aspect ratio preserved. Today only shrinking works on side handles; stretching does nothing because the math picks the unmoved axis.

Root cause recap

objects.js::applyTransform for type === 'emoji' does var s = Math.min(scaleX, scaleY). When a single side handle is dragged, the perpendicular axis stays at scale 1, so:

  • Shrink → min(<1, 1) = <1
  • Stretch → min(>1, 1) = 1

Fix

Pick whichever axis moved further from 1 — that is the axis the user actually dragged. This preserves the "single uniform scale" intent (no squashing) while always reflecting the user's drag.

Files to Modify

  • crates/hero_whiteboard_ui/static/web/js/whiteboard/objects.js — replace the Math.min line in the type === 'emoji' branch with a dominant-axis pick.

Implementation Plan

Step 1: Pick the dominant scale axis

File: crates/hero_whiteboard_ui/static/web/js/whiteboard/objects.js

Replace:

var s = Math.min(scaleX, scaleY);

with:

// Use whichever axis moved further from 1, so a one-side drag (which
// leaves the perpendicular axis at scale 1) still resizes the emoji.
// Min-of-two would pick the unmoved axis when stretching, blocking the
// resize.
var dx = Math.abs(scaleX - 1);
var dy = Math.abs(scaleY - 1);
var s = dx >= dy ? scaleX : scaleY;

Dependencies: none.

Acceptance Criteria

  • Drag a right/left/top/bottom handle outward — emoji grows.
  • Drag a right/left/top/bottom handle inward — emoji shrinks (unchanged).
  • Drag a corner handle in either direction — emoji grows / shrinks proportionally (unchanged).
  • Aspect ratio stays 1:1; the glyph never looks squashed regardless of which handle was used.
  • cargo fmt, cargo clippy --workspace --all-targets -- -D warnings, cargo test --workspace --lib clean.

Notes

  • Both axes still feed into the resize so corner drags pick the larger movement, but a side drag always uses the dragged axis directly. The 8-pixel font-size floor stays unchanged.
  • Corner drag with mismatched scales (e.g. scaleX = 1.5, scaleY = 1.2) will resize using the larger movement (1.5). That matches the user's gesture intent and keeps aspect ratio preserved.
## Implementation Spec for Issue #119 ### Objective Side-handle resize of an emoji must work in both directions (stretch and shrink), with aspect ratio preserved. Today only shrinking works on side handles; stretching does nothing because the math picks the unmoved axis. ### Root cause recap `objects.js::applyTransform` for `type === 'emoji'` does `var s = Math.min(scaleX, scaleY)`. When a single side handle is dragged, the perpendicular axis stays at scale 1, so: - Shrink → `min(<1, 1) = <1` ✓ - Stretch → `min(>1, 1) = 1` ✗ ### Fix Pick whichever axis moved further from 1 — that is the axis the user actually dragged. This preserves the "single uniform scale" intent (no squashing) while always reflecting the user's drag. ### Files to Modify - `crates/hero_whiteboard_ui/static/web/js/whiteboard/objects.js` — replace the `Math.min` line in the `type === 'emoji'` branch with a dominant-axis pick. ### Implementation Plan #### Step 1: Pick the dominant scale axis File: `crates/hero_whiteboard_ui/static/web/js/whiteboard/objects.js` Replace: ```js var s = Math.min(scaleX, scaleY); ``` with: ```js // Use whichever axis moved further from 1, so a one-side drag (which // leaves the perpendicular axis at scale 1) still resizes the emoji. // Min-of-two would pick the unmoved axis when stretching, blocking the // resize. var dx = Math.abs(scaleX - 1); var dy = Math.abs(scaleY - 1); var s = dx >= dy ? scaleX : scaleY; ``` Dependencies: none. ### Acceptance Criteria - [ ] Drag a right/left/top/bottom handle outward — emoji grows. - [ ] Drag a right/left/top/bottom handle inward — emoji shrinks (unchanged). - [ ] Drag a corner handle in either direction — emoji grows / shrinks proportionally (unchanged). - [ ] Aspect ratio stays 1:1; the glyph never looks squashed regardless of which handle was used. - [ ] `cargo fmt`, `cargo clippy --workspace --all-targets -- -D warnings`, `cargo test --workspace --lib` clean. ### Notes - Both axes still feed into the resize so corner drags pick the larger movement, but a side drag always uses the dragged axis directly. The 8-pixel font-size floor stays unchanged. - Corner drag with mismatched scales (e.g. `scaleX = 1.5, scaleY = 1.2`) will resize using the larger movement (1.5). That matches the user's gesture intent and keeps aspect ratio preserved.
Author
Member

Test Results

  • cargo fmt --all -- --check — clean
  • cargo clippy --workspace --all-targets -- -D warnings — clean
  • cargo test --workspace --lib — 0 failed
  • node --check objects.js — clean
## Test Results - `cargo fmt --all -- --check` — clean - `cargo clippy --workspace --all-targets -- -D warnings` — clean - `cargo test --workspace --lib` — 0 failed - `node --check objects.js` — clean
Author
Member

Implementation Summary

objects.js::applyTransform for type === 'emoji' was using Math.min(scaleX, scaleY) to pick a uniform scale factor. With single-side handle drags the perpendicular axis stays at 1, so a stretch (scaleX = 2, scaleY = 1) collapsed to min = 1 — no resize. Shrinks (scaleX = 0.5, scaleY = 1) worked because min = 0.5.

crates/hero_whiteboard_ui/static/web/js/whiteboard/objects.js

Replaced the Math.min line with a dominant-axis pick:

var dx = Math.abs(scaleX - 1);
var dy = Math.abs(scaleY - 1);
var s = dx >= dy ? scaleX : scaleY;

Whichever axis moved further from 1 wins — that's the axis the user actually dragged. The 8-pixel font-size floor and the bg width/height refresh from the rendered glyph stay unchanged.

Files Changed

  • crates/hero_whiteboard_ui/static/web/js/whiteboard/objects.js+5 / -3

Test Results

  • cargo fmt --all -- --check — clean
  • cargo clippy --workspace --all-targets -- -D warnings — clean
  • cargo test --workspace --lib — 0 failed
  • node --check objects.js — clean

Manual smoke

  1. Drop an emoji, drag the right handle outward — emoji grows. Same for left / top / bottom outward drag.
  2. Drag any side handle inward — emoji shrinks (unchanged behavior).
  3. Corner drag in either direction — proportional resize (unchanged).
  4. Aspect ratio stays 1:1 across all paths; the glyph never looks squashed.

Notes

  • Mixed corner drag (e.g. scaleX = 1.5, scaleY = 1.2) picks the larger movement (1.5). That matches user intent and keeps aspect ratio uniform.
## Implementation Summary `objects.js::applyTransform` for `type === 'emoji'` was using `Math.min(scaleX, scaleY)` to pick a uniform scale factor. With single-side handle drags the perpendicular axis stays at 1, so a stretch (`scaleX = 2, scaleY = 1`) collapsed to `min = 1` — no resize. Shrinks (`scaleX = 0.5, scaleY = 1`) worked because `min = 0.5`. ### `crates/hero_whiteboard_ui/static/web/js/whiteboard/objects.js` Replaced the `Math.min` line with a dominant-axis pick: ```js var dx = Math.abs(scaleX - 1); var dy = Math.abs(scaleY - 1); var s = dx >= dy ? scaleX : scaleY; ``` Whichever axis moved further from 1 wins — that's the axis the user actually dragged. The 8-pixel font-size floor and the bg width/height refresh from the rendered glyph stay unchanged. ### Files Changed - `crates/hero_whiteboard_ui/static/web/js/whiteboard/objects.js` — `+5 / -3` ### Test Results - `cargo fmt --all -- --check` — clean - `cargo clippy --workspace --all-targets -- -D warnings` — clean - `cargo test --workspace --lib` — 0 failed - `node --check objects.js` — clean ### Manual smoke 1. Drop an emoji, drag the right handle outward — emoji grows. Same for left / top / bottom outward drag. 2. Drag any side handle inward — emoji shrinks (unchanged behavior). 3. Corner drag in either direction — proportional resize (unchanged). 4. Aspect ratio stays 1:1 across all paths; the glyph never looks squashed. ### Notes - Mixed corner drag (e.g. `scaleX = 1.5, scaleY = 1.2`) picks the larger movement (1.5). That matches user intent and keeps aspect ratio uniform.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
lhumina_code/hero_whiteboard#119
No description provided.