Is it posible to animate color change without using color property in QML? - qt

I have a QML component containing an Image{} object that has no "color: " property set, and the color is instead passed to "svg from string" function from a string type property, and is later used as the svg image's "fill" parameter. It looks like this:
property string color: "aliceblue"
I'm wondering if there's a way to animate the color change using ColorAnimation QML Type, because it targets the "color: " property of the object, and it's not used at all in my case. The format of the color doesn't matter as long as it's supported by XML. (could be #RRGGBBAA etc)

The ColorAnimation can be applied to any property, not necessary a color, for example, we can see the ColorAnimation being used on a text property:
import QtQuick
import QtQuick.Controls
Page {
Button {
anchors.centerIn: parent
ColorAnimation on text {
id: animation
from: "red"
to: "yellow"
duration: 1000
}
onClicked: animation.restart()
}
}
You can Try it Online!
In terms of recoloring an SVG, I always leverage from the icon property that can be found on many components, such as Button. The icon property has source, color, width and height. In the following example, we use icon.color to recolor a space invader:
import QtQuick
import QtQuick.Controls
Page {
IconButton {
anchors.centerIn: parent
height: 256
icon.source: "space-invader.svg"
ColorAnimation on icon.color {
id: animation
from: "red"
to: "yellow"
duration: 1000
}
onClicked: animation.restart()
}
}
// IconButton.qml
import QtQuick
import QtQuick.Controls
Item {
id: iconButton
width: height
height: 64
clip: true
property alias icon: btn.icon
property alias background: btn.background
signal clicked()
Button {
id: btn
anchors.centerIn: parent
background: Item { }
icon.width: parent.width
icon.height: parent.height
onClicked: iconButton.clicked()
}
}
// space-invader.svg
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16">
<rect x="4" y="4" width="1" height="1"/>
<rect x="10" y="4" width="1" height="1"/>
<rect x="5" y="5" width="1" height="1"/>
<rect x="9" y="5" width="1" height="1"/>
<rect x="4" y="6" width="7" height="1"/>
<rect x="3" y="7" width="2" height="1"/>
<rect x="6" y="7" width="3" height="1"/>
<rect x="10" y="7" width="2" height="1"/>
<rect x="2" y="8" width="11" height="1"/>
<rect x="2" y="9" width="1" height="1"/>
<rect x="4" y="9" width="7" height="1"/>
<rect x="12" y="9" width="1" height="1"/>
<rect x="2" y="10" width="1" height="1"/>
<rect x="4" y="10" width="1" height="1"/>
<rect x="10" y="10" width="1" height="1"/>
<rect x="12" y="10" width="1" height="1"/>
<rect x="5" y="11" width="2" height="1"/>
<rect x="8" y="11" width="2" height="1"/>
</svg>
You can Try it Online!
Another way to recolor an SVG, is through the use of data URI image:data/svg+xml, + svg_string so that you can generate a svg string on the fly, however, the way how this compiles causes flicker when used with ColorAnimation. In the following example we ColorAnimation on the color col property, but, we promptly type convert it to a string with col + "" when passing it to our getSvg() function. We make use of ES6 string interpolation to quickly embed this color into our target string.
import QtQuick
import QtQuick.Controls
Page {
Image {
anchors.centerIn: parent
property color col: "red"
width: 256
height: 256
sourceSize: Qt.size(width, height)
source: `data:image/svg+xml,` + getSvg(col + '')
ColorAnimation on col {
from: "red"
to: "yellow"
duration: 10000
}
}
function getSvg(col) {
return `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16">
<rect x="4" y="4" width="1" fill="${col}" height="1"/>
<rect x="10" y="4" width="1" fill="${col}" height="1"/>
<rect x="5" y="5" width="1" fill="${col}" height="1"/>
<rect x="9" y="5" width="1" fill="${col}" height="1"/>
<rect x="4" y="6" width="7" fill="${col}" height="1"/>
<rect x="3" y="7" width="2" fill="${col}" height="1"/>
<rect x="6" y="7" width="3" fill="${col}" height="1"/>
<rect x="10" y="7" width="2" fill="${col}" height="1"/>
<rect x="2" y="8" width="11" fill="${col}" height="1"/>
<rect x="2" y="9" width="1" fill="${col}" height="1"/>
<rect x="4" y="9" width="7" fill="${col}" height="1"/>
<rect x="12" y="9" width="1" fill="${col}" height="1"/>
<rect x="2" y="10" width="1" fill="${col}" height="1"/>
<rect x="4" y="10" width="1" fill="${col}" height="1"/>
<rect x="10" y="10" width="1" fill="${col}" height="1"/>
<rect x="12" y="10" width="1" fill="${col}" height="1"/>
<rect x="5" y="11" width="2" fill="${col}" height="1"/>
<rect x="8" y="11" width="2" fill="${col}" height="1"/>
</svg>`
}
}
You can Try it Online!

Related

SVG layer with hole

I have found an SVG layer on the net. There should be a transparent hole in it. But I don't know how to change the colour around the hole from black to e.g. #5BBB74.
<svg viewbox="0 0 100 50" width="100%">
<defs>
<mask id="mask" x="0" y="0" width="80" height="30">
<rect x="0" y="0" width="100" height="40" fill="#fff"/>
<circle cx="50" cy="20" r="10" />
</mask>
</defs>
<rect x="0" y="0" width="100" height="50" mask="url(#mask)" fill-opacity=""/>
</svg>
You can add fill property to the rect affected by the mask.
<svg viewbox="0 0 100 50" width="100%">
<defs>
<mask id="mask" x="0" y="0" width="80" height="30">
<rect x="0" y="0" width="100" height="40" fill="#fff"/>
<circle cx="50" cy="20" r="10" />
</mask>
</defs>
<rect x="0" y="0" width="100" height="50" mask="url(#mask)" fill-opacity="" fill="#5BBB74"/>
</svg>

SVG elements CSS transition inside <g> tag on Chrome

So I've created a page with some colorful SVG images in it, and I want them to be grayed at hormal state, and shows color while hovered.
svg {
width: 200px;
margin: 50px;
}
svg * {
transition: fill 1s;
}
svg:not(:hover) * {
fill: gray !important;
}
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 3 1">
<rect x="0" y="0" width="1" height="1" style="fill: red" />
<rect x="1" y="0" width="1" height="1" style="fill: green" />
<rect x="2" y="0" width="1" height="1" style="fill: blue" />
</svg>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 3 1">
<g>
<rect x="0" y="0" width="1" height="1" style="fill: red" />
<rect x="1" y="0" width="1" height="1" style="fill: green" />
<rect x="2" y="0" width="1" height="1" style="fill: blue" />
</g>
</svg>
As one can see, SVGs have different colored elements, also some elements are grouped. This is pretty simplified example, but the real images are much more complex, with massive transform-s so I can't easily remove grouping.
Both images work perfect and changes colors while hovered, but the first image does it instantly while the the second has 1 second delay before animation starts.
Searching for the solution I've found that if an elemend is wrapped with a single <g> tag it has the animation delay, but if there no <g> or two of them, no delay occurs.
Firefox animates both images with no animation delay.
By the moment I ungrouped elements by hands, but obviously it's not a good solution, so the question is how it can be solved without changing files at all ?
A pretty sneaky bug, but easily solved: just restrict the child selector to non-g elements:
svg {
width: 200px;
margin: 50px;
}
svg :not(g) {
transition: fill 1s;
}
svg:not(:hover) * {
fill: gray !important;
}
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 3 1">
<rect x="0" y="0" width="1" height="1" style="fill: red" />
<rect x="1" y="0" width="1" height="1" style="fill: green" />
<rect x="2" y="0" width="1" height="1" style="fill: blue" />
</svg>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 3 1">
<g>
<rect x="0" y="0" width="1" height="1" style="fill: red" />
<rect x="1" y="0" width="1" height="1" style="fill: green" />
<rect x="2" y="0" width="1" height="1" style="fill: blue" />
</g>
</svg>

SVG animate (rotate) all graphics around a center point

I have a simple graphic SVG
I would like it to rotate around the center point. It doesn't have to be smooth, just a rotation 90 and back every second.
<?xml version="1.0" encoding="utf-8"?>
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 27 27" style="enable-background:new 0 0 27 27;" xml:space="preserve">
<style type="text/css">
.st0{fill:#FFFFFF;}
</style>
<rect x="0" width="27" height="27"/>
<rect x="8" y="11" class="st0" width="1" height="5"/>
<rect x="9" y="10" class="st0" width="1" height="1"/>
<rect x="10" y="9" class="st0" width="1" height="1"/>
<rect x="11" y="8" class="st0" width="5" height="1"/>
<rect x="13" y="6" class="st0" width="1" height="1"/>
<rect x="14" y="7" class="st0" width="1" height="1"/>
<rect x="14" y="9" class="st0" width="1" height="1"/>
<rect x="13" y="10" class="st0" width="1" height="1"/>
<rect x="18" y="11" class="st0" width="1" height="5"/>
<rect x="17" y="16" class="st0" width="1" height="1"/>
<rect x="16" y="17" class="st0" width="1" height="1"/>
<rect x="11" y="18" class="st0" width="5" height="1"/>
<rect x="12" y="17" class="st0" width="1" height="1"/>
<rect x="13" y="16" class="st0" width="1" height="1"/>
<rect x="12" y="19" class="st0" width="1" height="1"/>
<rect x="13" y="20" class="st0" width="1" height="1"/>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
</svg>
Is there an animation where I can rotate these SVG graphics around the center point?
You can just use animateTransform:
.st0 {
fill: white;
}
<svg>
<g>
<animateTransform
attributeName="transform"
type="rotate"
values="0 14 14; 90 14 14; 0 14 14"
dur="1s"
repeatCount="indefinite" />
<rect x="0" width="27" height="27"/>
<rect x="8" y="11" class="st0" width="1" height="5"/>
<rect x="9" y="10" class="st0" width="1" height="1"/>
<rect x="10" y="9" class="st0" width="1" height="1"/>
<rect x="11" y="8" class="st0" width="5" height="1"/>
<rect x="13" y="6" class="st0" width="1" height="1"/>
<rect x="14" y="7" class="st0" width="1" height="1"/>
<rect x="14" y="9" class="st0" width="1" height="1"/>
<rect x="13" y="10" class="st0" width="1" height="1"/>
<rect x="18" y="11" class="st0" width="1" height="5"/>
<rect x="17" y="16" class="st0" width="1" height="1"/>
<rect x="16" y="17" class="st0" width="1" height="1"/>
<rect x="11" y="18" class="st0" width="5" height="1"/>
<rect x="12" y="17" class="st0" width="1" height="1"/>
<rect x="13" y="16" class="st0" width="1" height="1"/>
<rect x="12" y="19" class="st0" width="1" height="1"/>
<rect x="13" y="20" class="st0" width="1" height="1"/>
</g>
</svg>

SVG bars graph upside down without rotation

I have an svg rect chart like:
<div style="width:300px;height:300px;">
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" style="width:100%;height:100%" viewBox="0 0 300 300">
<g>
<rect width="14.55" height="40%" x="0" y="0" fill="black"></rect>
<rect width="14.55" height="20%" x="50" y="0" fill="green"></rect>
<rect width="14.55" height="80%" x="100" y="0" fill="red"></rect>
<rect width="14.55" height="90%" x="150" y="0" fill="yellow"></rect>
<rect width="14.55" height="10%" x="200" y="0" fill="pink"></rect>
<rect width="14.55" height="60%" x="250" y="0" fill="orange"></rect>
</g>
</svg>
</div>
What I want to do is to display it upside down.
The code and a given solution, are coming from http://jsfiddle.net/rhvP8/5/
Although, I want to keep each bar in the same X place. So, rotation approach is not that useful in this case.
Any help is welcome.
Rotation of the x-axis only seems be be what you require.
svg {
border: 1px solid green;
transform: rotateX(180deg);
}
<div style="width:300px;height:300px;">
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" style="width:100%;height:100%" viewBox="0 0 300 300">
<g>
<rect width="14.55" height="40%" x="0" y="0" fill="black"></rect>
<rect width="14.55" height="20%" x="50" y="0" fill="green"></rect>
<rect width="14.55" height="80%" x="100" y="0" fill="red"></rect>
<rect width="14.55" height="90%" x="150" y="0" fill="yellow"></rect>
<rect width="14.55" height="10%" x="200" y="0" fill="pink"></rect>
<rect width="14.55" height="60%" x="250" y="0" fill="orange"></rect>
</g>
</svg>
</div>

SVG clip path hole / inner cut

<clipPath id="clip1">
<rect x="10" y="222" height="30" width="50" rx="5" />
</clipPath>
<image .... clip-path="url(#clip1)" />
It cuts the outer space of the image. But I want to cut a hole into the image. How can I achieve it?
For your purposes, you can use a mask
<svg width="200" height="200" viewBox="0 0 200 200">
<defs>
<mask id="hole">
<rect width="100%" height="100%" fill="white"/>
<rect x="50" y="50" height="50" width="100" rx="5" fill="black" />
</mask>
</defs>
<image x="0" y="0" width="200" height="200"
xlink:href="http://placeimg.com/200/200/any"
mask="url(#hole)" >
</image>
</svg>

Resources