Dynamically Changing preserveAspectRatio
In Example 8-2 and the following sections in the book, we built a resizable inline SVG, for testing the effect of viewBox
and preserveAspectRatio
values in viewports of different sizes and aspect ratios.
Example 8-X1, goes one step further, creating a dynamic web page in which you can switch between the options using form inputs.
Example 8-X1. Dynamic demonstration of preserveAspectRatio
options
HTML markup:
<!DOCTYPE html>
<html
lang=
"en"
>
<head
>
<meta
charset=
"utf-8"
/>
<title
>
preserveAspectRatio options
</title>
<link
rel=
"stylesheet"
href=
"flex-scale-viewBox.css"
/>
<style
>
/* additional styles for form */
form
{
max-width
:
25em
;
text-align
:
right
;
}
label
{
display
:
block
;
margin
:
0.2em
0
;
}
</style>
<script
src=
"flex-scale-preserve-options.js"
>
</script>
</head>
<body
>
<form
id=
"preserve-options"
>
<label
>
<code
>
preserveAspectRatio
</code>
mode
<select
id=
"preserve"
>
<option
value=
"none"
>
none
</option>
<option
value=
"meet"
selected
>
meet
</option>
<option
value=
"slice"
>
slice
</option>
</select>
</label>
<label
>
<i
>
x
</i>
-alignment
<select
id=
"x-align"
>
<option
value=
"xMin"
>
min
</option>
<option
value=
"xMid"
selected
>
mid
</option>
<option
value=
"xMax"
>
max
</option>
</select>
</label>
<label
>
<i
>
y
</i>
-alignment
<select
id=
"y-align"
>
<option
value=
"YMin"
>
min
</option>
<option
value=
"YMid"
selected
>
mid
</option>
<option
value=
"YMax"
>
max
</option>
</select>
</label>
</form>
<div
class=
"wrapper"
>
<svg
id=
"preserve-svg"
viewBox=
"0,0 20,20"
>
<title
>
Spade
</title>
<path
d=
"M9,15C9,20 0,21 0,16S6,9 10,0C14,9 20,11 20,16 S11,20 11,15Q11,20 13,20H7Q9,20 9,15Z"
/>
</svg>
</div>
</body>
</html>
-
The stylesheet from Example 8-2 is re-used, to create the resizable SVG. That could include any modifications you added to make the SVG resizable without the CSS
resize
property. -
A linked script will add the behavior to listen for user input and update the SVG accordingly.
-
The webpage includes a
<form>
with the three drop-down lists for each of the three independent parts of thepreserveAspectRatio
attribute. The lists are created using the HTML<select>
and<option>
elements. A<label>
wrapped around each selection set associates the description with the form field. -
The
meet
option is selected by default, to match the defaultpreserveAspectRatio
value on the SVG. -
For the x- and y-alignment options, the
value
attribute for each<option>
is the exact keyword that will be used in the finalpreserveAspectRatio
value (including capitalization), but the visible text is more readable. Again, the defaultxMidYMid
options are checked.
JavaScript: flex-scale-preserve-options.js
window
.
addEventListener
(
"load"
,
function
(
)
{
var
doc
=
document
;
var
svg
=
doc
.
getElementById
(
"preserve-svg"
)
;
var
preserve
=
doc
.
getElementById
(
"preserve"
)
;
var
xAlign
=
doc
.
getElementById
(
"x-align"
)
;
var
yAlign
=
doc
.
getElementById
(
"y-align"
)
;
doc
.
getElementById
(
"preserve-options"
)
.
addEventListener
(
"change"
,
update
)
;
function
update
(
event
)
{
var
p
,
string
;
p
=
preserve
.
value
;
if
(
p
==
"none"
)
{
xAlign
.
disabled
=
yAlign
.
disabled
=
true
;
string
=
p
;
}
else
{
xAlign
.
disabled
=
yAlign
.
disabled
=
false
;
string
=
xAlign
.
value
+
yAlign
.
value
+
" "
+
p
;
}
svg
.
setAttribute
(
"preserveAspectRatio"
,
string
)
;
}
update
(
)
;
}
)
;
-
As usual, the script is contained inside an anonymous function. However, instead of running the function automatically, it is set to run when the document finishes loading (and the
load
event is triggered on the window). The script can therefore be included in the HTML<head>
, instead of at the end, and it will still not run until all the elements had been initialized. -
All the key elements in the markup have
id
attributes, so they are easy to access from the script. They won’t change, so they are selected once and stored in variables. Thedoc
variable is just a convenient alias fordocument
, to reduce typing. -
A single listener function will react to all changes within the form, regardless of which input element triggered the change. We don’t need to access the form again, so it isn’t saved to a variable; instead, the
addEventListener
method is called directly on the object returned bygetElementById
. -
The
update(event)
function determines the currently selected options in the form, and sets the SVG’spreserveAspectRatio
accordingly. Thevalue
property of anHTMLSelectElement
is equal to thevalue
attribute of the currently selected<option>
. -
If the selected mode is
none
, the alignment options are disabled. These only have an effect formeet
andslice
. -
For options other than
none
, the current alignment selections are accessed, and all three keywords are composed into a singlepreserveAspectRatio
string (paying attention to the spacing between values). -
The update function is also called once when the script is run after loading. That way, the SVG matches the form selections if the browser remembers choices from a previous visit to the web page.
The web page, with the SVG set to xMinYMax slice
is displayed in Figure 8-X1. To get the complete effect, view the live example in your browser. Try out all the options, and resize the SVG for each.
As you experiment, it should become clear that only one of the alignment options has an effect at a time. The graphic always exactly fits one dimension of the available space. Whether its the x or y dimension is determined both by the shape of the space, and by meet
versus slice
.
In Chapter 10, we look at preserveAspectRatio
again, for images. We can, of course, adapt the form and script from Example 8-X1 to instead set the attribute on the <image>
element. Example 8-X2 does that, using the multi-image layout from Example 10-4.
Example 8-X2. Demonstrating preserveAspectRatio
options on embedded images, dynamically
<!DOCTYPE html>
<html
lang=
"en"
>
<head
>
<meta
charset=
"utf-8"
/>
<title
>
preserveAspectRatio for Images
</title>
<style
>
body
{
margin
:
0
;
}
form
{
max-width
:
400px
;
text-align
:
right
;
}
label
{
display
:
block
;
margin
:
0.2em
0
;
}
svg
{
display
:
block
;
max-height
:
calc
(
100vh
-
5em
);
max-width
:
100%
;
}
</style>
</head>
<body
>
<form
id=
"preserve-options"
>
<label
>
<code
>
preserveAspectRatio
</code>
mode
<select
id=
"preserve"
>
<option
value=
"none"
>
none
</option>
<option
value=
"meet"
selected
>
meet
</option>
<option
value=
"slice"
>
slice
</option>
</select>
</label>
<label
>
<i
>
x
</i>
-alignment
<select
id=
"x-align"
>
<option
value=
"xMin"
>
min
</option>
<option
value=
"xMid"
selected
>
mid
</option>
<option
value=
"xMax"
>
max
</option>
</select>
</label>
<label
>
<i
>
y
</i>
-alignment
<select
id=
"y-align"
>
<option
value=
"YMin"
>
min
</option>
<option
value=
"YMid"
selected
>
mid
</option>
<option
value=
"YMax"
>
max
</option>
</select>
</label>
</form>
<svg
xmlns=
"http://www.w3.org/2000/svg"
xml:lang=
"en"
xmlns:xlink=
"http://www.w3.org/1999/xlink"
width=
"400"
height=
"600"
viewBox=
"0 0 400 600"
>
<style
>
text
{
font
:
20px
Tahoma
,
sans-serif
;
text-anchor
:
middle
;
fill
:
darkRed
;
}
.bird-colors
rect
{
fill
:
#5f6cb9
;
}
.bird-colors
text
{
fill
:
#bbde60
;
}
.theme-colors
rect
{
fill
:
#00aaa9
;
}
.theme-colors
text
{
fill
:
white
;
}
</style>
<svg
viewBox=
"50 5 500 750"
width=
"50%"
height=
"50%"
>
<image
height=
"900"
width=
"600"
class=
"preserve-image"
aria-label=
"Using SVG cover image"
xlink:href=
"using_svg_cover.png"
/>
</svg>
<g
class=
"bird-colors"
>
<rect
x=
"50%"
height=
"50%"
width=
"50%"
/>
<text
x=
"75%"
y=
"45"
dy=
"-0.5em"
>
Amelia
<tspan
x=
"75%"
dy=
"1em"
>
Bellamy-Royds
</tspan>
</text>
<image
x=
"50%"
y=
"80"
height=
"220"
width=
"200"
class=
"preserve-image"
aria-label=
"Amelia in the sunshine"
xlink:href=
"Amelia.jpg"
/>
</g>
<g
class=
"bird-colors"
>
<rect
y=
"50%"
height=
"50%"
width=
"50%"
/>
<text
x=
"25%"
y=
"345"
>
Kurt Cagle
</text>
<image
y=
"380"
height=
"220"
width=
"200"
class=
"preserve-image"
aria-label=
"Kurt in a top hat"
xlink:href=
"Kurt.jpg"
/>
</g>
<g
class=
"theme-colors"
>
<rect
x=
"50%"
y=
"50%"
height=
"50%"
width=
"50%"
/>
<text
x=
"75%"
y=
"345"
>
Dudley Storey
</text>
<image
x=
"50%"
y=
"380"
height=
"220"
width=
"200"
class=
"preserve-image"
aria-label=
"Dudley, re-imagined as a tiny warrior"
xlink:href=
"Dudley.jpg"
/>
</g>
</svg>
<script
>
window
.
addEventListener
(
"load"
,
function
(
)
{
var
doc
=
document
;
var
images
=
doc
.
getElementsByClassName
(
"preserve-image"
)
;
var
preserve
=
doc
.
getElementById
(
"preserve"
)
;
var
xAlign
=
doc
.
getElementById
(
"x-align"
)
;
var
yAlign
=
doc
.
getElementById
(
"y-align"
)
;
doc
.
getElementById
(
"preserve-options"
)
.
addEventListener
(
"change"
,
update
)
;
function
update
(
event
)
{
var
p
,
string
;
p
=
preserve
.
value
;
if
(
p
==
"none"
)
{
xAlign
.
disabled
=
yAlign
.
disabled
=
true
;
string
=
p
;
}
else
{
xAlign
.
disabled
=
yAlign
.
disabled
=
false
;
string
=
xAlign
.
value
+
yAlign
.
value
+
" "
+
p
;
}
for
(
var
i
=
0
,
n
=
images
.
length
;
i
<
n
;
i
++
)
{
images
[
i
]
.
setAttribute
(
"preserveAspectRatio"
,
string
)
;
}
}
update
(
)
;
}
)
;
</script>
</body>
</html>
-
The form hasn’t changed from Example 8-X1; since the embedded images are not SVG, the
defer
option is not relevant forpreserveAspectRatio
. -
The main SVG has a fixed height and width in the markup, but CSS
max-height
andmax-width
will scale it down to fit the screen. -
Each image is identified by the class
preserve-image
to allow it to be selected in the script. (However, the book-cover image exactly matches its aspect ratio, and won’t ever change.) -
All three photographs are given the same dimensions, in the SVG’s coordinate system: 200px wide and 220px tall.
-
Because we are modifying multiple elements, the script has been changed to cycle through them in a loop; otherwise, it is the same as Example 8-X1.