Skip to content

Commit d16dd5d

Browse files
feat: add naturalWidth and naturalHeight bindings (#7857)
Closes #7771 --------- Co-authored-by: Simon H <[email protected]> Co-authored-by: Simon Holthausen <[email protected]>
1 parent ba8f979 commit d16dd5d

File tree

6 files changed

+133
-1
lines changed

6 files changed

+133
-1
lines changed

site/content/docs/03-template-syntax.md

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -751,6 +751,22 @@ Videos additionally have readonly `videoWidth` and `videoHeight` bindings.
751751
></video>
752752
```
753753

754+
##### Image element bindings
755+
756+
---
757+
758+
Image elements (`<img>`) have two readonly bindings:
759+
760+
* `naturalWidth` (readonly) — the original width of the image, available after the image has loaded
761+
* `naturalHeight` (readonly) — the original height of the image, available after the image has loaded
762+
763+
```sv
764+
<img
765+
bind:naturalWidth
766+
bind:naturalHeight
767+
></img>
768+
```
769+
754770
##### Block-level element bindings
755771

756772
---

src/compiler/compile/nodes/Binding.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,9 @@ const read_only_media_attributes = new Set([
2222
'seeking',
2323
'ended',
2424
'videoHeight',
25-
'videoWidth'
25+
'videoWidth',
26+
'naturalWidth',
27+
'naturalHeight'
2628
]);
2729

2830
export default class Binding extends Node {

src/compiler/compile/nodes/Element.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -918,6 +918,13 @@ export default class Element extends Node {
918918
} else if (is_void(this.name)) {
919919
return component.error(binding, compiler_errors.invalid_binding_on(binding.name, `void elements like <${this.name}>. Use a wrapper element instead`));
920920
}
921+
} else if (
922+
name === 'naturalWidth' ||
923+
name === 'naturalHeight'
924+
) {
925+
if (this.name !== 'img') {
926+
return component.error(binding, compiler_errors.invalid_binding_element_with('<img>', name));
927+
}
921928
} else if (
922929
name === 'textContent' ||
923930
name === 'innerHTML'

src/compiler/compile/render_dom/wrappers/Element/index.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,10 @@ const events = [
137137
event_names: ['toggle'],
138138
filter: (node: Element, _name: string) =>
139139
node.name === 'details'
140+
},
141+
{
142+
event_names: ['load'],
143+
filter: (_: Element, name: string) => name === 'naturalHeight' || name === 'naturalWidth'
140144
}
141145
];
142146

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
/* generated by Svelte vX.Y.Z */
2+
import {
3+
SvelteComponent,
4+
add_render_callback,
5+
attr,
6+
detach,
7+
element,
8+
init,
9+
insert,
10+
listen,
11+
noop,
12+
safe_not_equal,
13+
set_data,
14+
space,
15+
src_url_equal,
16+
text
17+
} from "svelte/internal";
18+
19+
function create_fragment(ctx) {
20+
let img;
21+
let img_src_value;
22+
let t0;
23+
let t1;
24+
let t2;
25+
let t3;
26+
let mounted;
27+
let dispose;
28+
29+
return {
30+
c() {
31+
img = element("img");
32+
t0 = space();
33+
t1 = text(/*naturalWidth*/ ctx[0]);
34+
t2 = text(" x ");
35+
t3 = text(/*naturalHeight*/ ctx[1]);
36+
if (!src_url_equal(img.src, img_src_value = "something.jpg")) attr(img, "src", img_src_value);
37+
if (/*naturalWidth*/ ctx[0] === void 0 || /*naturalHeight*/ ctx[1] === void 0) add_render_callback(() => /*img_load_handler*/ ctx[2].call(img));
38+
},
39+
m(target, anchor) {
40+
insert(target, img, anchor);
41+
insert(target, t0, anchor);
42+
insert(target, t1, anchor);
43+
insert(target, t2, anchor);
44+
insert(target, t3, anchor);
45+
46+
if (!mounted) {
47+
dispose = listen(img, "load", /*img_load_handler*/ ctx[2]);
48+
mounted = true;
49+
}
50+
},
51+
p(ctx, [dirty]) {
52+
if (dirty & /*naturalWidth*/ 1) set_data(t1, /*naturalWidth*/ ctx[0]);
53+
if (dirty & /*naturalHeight*/ 2) set_data(t3, /*naturalHeight*/ ctx[1]);
54+
},
55+
i: noop,
56+
o: noop,
57+
d(detaching) {
58+
if (detaching) detach(img);
59+
if (detaching) detach(t0);
60+
if (detaching) detach(t1);
61+
if (detaching) detach(t2);
62+
if (detaching) detach(t3);
63+
mounted = false;
64+
dispose();
65+
}
66+
};
67+
}
68+
69+
function instance($$self, $$props, $$invalidate) {
70+
let { naturalWidth = 0 } = $$props;
71+
let { naturalHeight = 0 } = $$props;
72+
73+
function img_load_handler() {
74+
naturalWidth = this.naturalWidth;
75+
naturalHeight = this.naturalHeight;
76+
$$invalidate(0, naturalWidth);
77+
$$invalidate(1, naturalHeight);
78+
}
79+
80+
$$self.$$set = $$props => {
81+
if ('naturalWidth' in $$props) $$invalidate(0, naturalWidth = $$props.naturalWidth);
82+
if ('naturalHeight' in $$props) $$invalidate(1, naturalHeight = $$props.naturalHeight);
83+
};
84+
85+
return [naturalWidth, naturalHeight, img_load_handler];
86+
}
87+
88+
class Component extends SvelteComponent {
89+
constructor(options) {
90+
super();
91+
init(this, options, instance, create_fragment, safe_not_equal, { naturalWidth: 0, naturalHeight: 1 });
92+
}
93+
}
94+
95+
export default Component;
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
<script>
2+
export let naturalWidth = 0;
3+
export let naturalHeight = 0;
4+
</script>
5+
6+
<img src="something.jpg" bind:naturalWidth bind:naturalHeight>
7+
8+
{naturalWidth} x {naturalHeight}

0 commit comments

Comments
 (0)