Skip to content

Commit

Permalink
feat: image knob
Browse files Browse the repository at this point in the history
  • Loading branch information
eye-wave committed Nov 26, 2024
1 parent c7859a2 commit 38c19ac
Show file tree
Hide file tree
Showing 11 changed files with 700 additions and 226 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "svelte-knobs",
"description": "Svelte component library for building customizable knob controls.",
"version": "0.3.0",
"version": "0.3.1",
"repository": {
"url": "https://github.com/eye-wave/svelte-knobs"
},
Expand Down
137 changes: 137 additions & 0 deletions src/lib/ImageKnob.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
<script lang="ts">
import { normalize } from './params.js';
import { onMount } from 'svelte';
import { spring } from 'svelte/motion';
import KnobBase from './KnobBase.svelte';
import type { SharedKnobProps } from './KnobBase.svelte';
type Props = {
stiffness?: number;
class?: string;
source: string;
numberOfFrames?: number;
width?: number;
height?: number;
} & SharedKnobProps;
let {
style,
source,
numberOfFrames = $bindable(),
class: className,
label = '',
unit = '',
onChange,
value = $bindable(),
defaultValue,
param,
stiffness = 0.5,
decimalDigits = 0,
snapValues = [],
snapThreshold = 0.1,
width = 80,
height = 80,
disabled = false,
draggable = true,
colors = {}
}: Props = $props();
const rotationDegrees = spring(normalize(value, param) * 270 - 135, { stiffness });
function draw() {
if (!ctx) return;
if (!numberOfFrames) return;
if (!image) return;
if (!('width' in image && 'height' in image)) return;
const normalized = ($rotationDegrees + 135) / 270;
const i = Math.floor(normalized * numberOfFrames);
const y = image.width * i;
if (i < 0) return;
if (y >= image.height) return;
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.drawImage(
image,
0, // sx
y, // sy
image.width, // sWidth
image.width, // sHeight
0, // dx
0, // dy
canvas.width, // dWidth
canvas.height // dHeight
);
}
onMount(() => {
ctx = canvas.getContext('2d');
ctx?.fillText('Loading...', 0, canvas.height / 2);
});
onMount(() => {
if (!source) return;
console.time('load image');
image = new Image();
image.src = source;
image.onload = () => {
if (!image) throw Error('What');
if (numberOfFrames === undefined) {
if ('width' in image && 'height' in image) {
console.warn('Automatic estimation of numberOfFrames might be inaccurate');
numberOfFrames = Math.floor(image.height / image.width);
} else {
throw Error('Failed to estimate numberOfFrames');
}
}
console.timeEnd('load image');
isLoading = false;
};
});
$effect(() => {
if (isLoading) return;
draw();
});
let canvas: HTMLCanvasElement;
let ctx: CanvasRenderingContext2D | null = null;
let isLoading = $state(true);
let image: HTMLImageElement | null = null;
</script>

<KnobBase
{colors}
{decimalDigits}
{defaultValue}
{disabled}
{draggable}
{label}
{onChange}
{param}
{snapThreshold}
{snapValues}
{style}
{unit}
bind:value
{rotationDegrees}
>
{#snippet ui({ handleTouchStart, handleMouseDown, handleDblClick })}
<canvas
bind:this={canvas}
{width}
{height}
class={className}
onmousedown={handleMouseDown}
ontouchstart={handleTouchStart}
ondblclick={handleDblClick}
oncontextmenu={(e) => e.preventDefault()}
draggable={false}
>
</canvas>
{/snippet}
</KnobBase>
Loading

0 comments on commit 38c19ac

Please sign in to comment.