Reputation: 2796
I know there are several questions on here about scaling an <image>
and sorts on the centre of said element but I have a slightly different problem.
My problem being I have a <pattern>
which contains my <image>
as it is applied dynamically to other SVGs. I'm using the patternTransform
attribute to scale and translate the image with a matrix (rather than individual transforms). I need to scale the image around the centre of the image although my understanding is that <pattern>
is an infinite canvas of it's contents.
I have tried the technique outlined here (Transforming Coordinate system)
summary of the link
X = centreX * (scale_factor -1)
Y = centreY * (scale_factor -1)
It has certainly made a difference to the way the image appears to translate but the origin looks more like it is about 15% from the top/left as opposed to the centre of the proverbial <image>
.
How do I scale the image without translating it while the image resides within a <pattern>
and used as a fill
?
Below is the code I'm using to scale the image and a gif of how it's behaving.
pattern definition
<pattern id="user_image_container" patternUnits="objectBoundingBox" x="0" y="0">
<image xlink:href="https://upload.wikimedia.org/wikipedia/commons/8/8a/Free_Unedited_Happy_Little_Yellow_Stars_in_Pink_Flower_Creative_Commons_(2898759838).jpg" id="user_image"></image>
</pattern>
Javascript controlling the matrix
/**
* Scale the image. Called by an input event from an input[type="range"]
* @param {Event} event calling this function.
* @return {void}
*/
update_image_scale: function update_image_scale(event) {
// Stop anything automatic from happening.
event.preventDefault()
// Get the input and previous value.
const target_input = $(event.target)
const target_size = parseFloat(target_input.val())
const image_size = App.canvas_view.image_size // Original size of the image.
// Get the target <pattern>.
const target_image = $("#user_image_container").get(0)
// Get the centred X/Y position of the image.
const cx = parseFloat(target_image.getAttribute("data-x") || 0) + (image_size.width / 2)
const cy = parseFloat(target_image.getAttribute("data-y") || 0) + (image_size.height / 2)
// Get the new translation position.
const x = -cx * (target_size - 1)
const y = -cy * (target_size - 1)
console.log(x, y)
// Set the new X/Y position.
target_image.setAttribute("data-x", x)
target_image.setAttribute("data-y", y)
// Redraw the image.
$("#user_image_container").get(0).setAttribute("patternTransform", `matrix(${target_size} 0 0 ${target_size} ${x} ${y})`)
},
Here's how it behaves at the moment (sorry for the large gif):
This is controlled with an input[type="range"]
input
event, it starts to get wild as the scale goes > 2.
Upvotes: 1
Views: 340
Reputation: 2796
After leaving this alone for a few days, I finally sussed it.
The way to centre an <image>
in a <pattern>
is to not try and (logically) use the <image>
to base the translate
on but in-fact use the <pattern>
to base the translate
on.
I.E
/**
* Scale the image.
* @param {Event} event calling this function.
* @return {void}
*/
update_image_scale: function update_image_scale(event) {
// Stop anything automatic from happening.
event.preventDefault()
// Get the mask dimensions.
const mask_container_size = $("#image_mask").get(0).getBBox()
// Get the target image.
const target_image = $("#user_image_container").get(0)
// Get the input and previous value.
const target_size = parseFloat($(event.target).val())
// Get the new translation position.
const x = (-mask_container_size.width / 2) * (target_size - 1)
const y = (-mask_container_size.height / 2) * (target_size - 1)
// Set the new X/Y position.
target_image.setAttribute("data-scale-x", x)
target_image.setAttribute("data-scale-y", y)
// Redraw the image.
global.App.canvas_view.render_image_in_pattern()
}
And the matrix controller is now individual transforms.
/**
* Render the image with all transforms.
* @return {void}
*/
render_image_in_pattern: function render_image_in_pattern() {
// Get the target image.
const target = this.$("#user_image_container").get(0)
// Get the image coordinates.
const sx = parseFloat(target.getAttribute("data-scale-x")) || 0
const sy = parseFloat(target.getAttribute("data-scale-y")) || 0
const x = parseFloat(target.getAttribute("data-x")) || 0
const y = parseFloat(target.getAttribute("data-y")) || 0
// Get the rotation.
const r = this.rotation || 0
// Get the scale from the range field.
const s = $(".image-scaler").val()
// Translate the image.
$("#user_image_container").get(0)
.setAttribute("patternTransform", `translate(${sx} ${sy}) rotate(${r}) scale(${s}) translate(${x} ${y})`)
},
The data-x
and data-y
attributes are set by another function for drag and drop purposes.
Upvotes: 2