Interactive SVG background: Allow processing whole SVG group for state (#2872)
Allows to flash and handle state on whole SVG groups without a proxy by using the group's path elements instead, e.g. this whole group can be used directly --------- Signed-off-by: Stefan Höhn <mail@stefanhoehn.com>pull/2878/head
parent
e3daf94577
commit
e1990669e0
|
@ -145,6 +145,22 @@ export default {
|
||||||
* @param {HTMLElement} el
|
* @param {HTMLElement} el
|
||||||
*/
|
*/
|
||||||
svgOnMouseOver (el) {
|
svgOnMouseOver (el) {
|
||||||
|
function flashElement (el, fillColor) {
|
||||||
|
if (el && !el.flashing) {
|
||||||
|
const attributeName = (el.style.fill !== 'none') ? 'fill' : 'stroke'
|
||||||
|
const oldFill = el.style.getPropertyValue(attributeName)
|
||||||
|
const oldOpacity = el.style.opacity
|
||||||
|
el.style.setProperty(attributeName, fillColor)
|
||||||
|
el.style.opacity = 1
|
||||||
|
el.flashing = true
|
||||||
|
setTimeout(() => {
|
||||||
|
el.style.setProperty(attributeName, oldFill)
|
||||||
|
el.style.opacity = oldOpacity
|
||||||
|
el.flashing = false
|
||||||
|
}, 200)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (this.context.editmode || (!this.context.editmode && this.config.embedSvgFlashing)) {
|
if (this.context.editmode || (!this.context.editmode && this.config.embedSvgFlashing)) {
|
||||||
const tagName = el.tagName
|
const tagName = el.tagName
|
||||||
// fill green if item config is available, red if config is still missing
|
// fill green if item config is available, red if config is still missing
|
||||||
|
@ -160,19 +176,15 @@ export default {
|
||||||
el.style.setProperty(attributeName, oldFill)
|
el.style.setProperty(attributeName, oldFill)
|
||||||
}, 200)
|
}, 200)
|
||||||
} else { // groups cannot be filled, so we need to fill special element marked as "flash"
|
} else { // groups cannot be filled, so we need to fill special element marked as "flash"
|
||||||
const flashElement = el.querySelector('[flash]')
|
const elementToFlash = el.querySelector('[flash]')
|
||||||
if (flashElement && !flashElement.flashing) {
|
if (elementToFlash) {
|
||||||
const attributeName = (flashElement.style.fill !== 'none') ? 'fill' : 'stroke'
|
flashElement(elementToFlash, fillColor)
|
||||||
const oldFill = flashElement.style.getPropertyValue(attributeName)
|
} else {
|
||||||
const oldOpacity = flashElement.style.opacity
|
// let's try flashing all path elements in the group
|
||||||
flashElement.style.setProperty(attributeName, fillColor)
|
const flashElements = el.querySelectorAll('path')
|
||||||
flashElement.style.opacity = 1
|
for (const path of flashElements) {
|
||||||
flashElement.flashing = true
|
flashElement(path, fillColor)
|
||||||
setTimeout(() => {
|
}
|
||||||
flashElement.style.setProperty(attributeName, oldFill)
|
|
||||||
flashElement.style.opacity = oldOpacity
|
|
||||||
flashElement.flashing = false
|
|
||||||
}, 200)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -292,6 +304,92 @@ export default {
|
||||||
svgElement.innerHTML = state
|
svgElement.innerHTML = state
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function processState (useProxy, element) {
|
||||||
|
if (state === 'ON' || stateType === 'HSB') {
|
||||||
|
if (useProxy && svgElementConfig.stateAsOpacity) { // we use the flash element
|
||||||
|
let opacity = (state === 'ON') ? 1 : 0
|
||||||
|
opacity = (svgElementConfig.invertStateOpacity) ? 1 - opacity : opacity
|
||||||
|
opacity = (opacity < svgElementConfig.stateMinOpacity) ? svgElementConfig.stateMinOpacity : opacity
|
||||||
|
element.style.opacity = opacity
|
||||||
|
} else {
|
||||||
|
element.oldFill = element.style.fill
|
||||||
|
element.style.fill = stateOnColorRgbStyle
|
||||||
|
}
|
||||||
|
if (svgElementConfig.stateOnAsStyleClass) {
|
||||||
|
if (svgElementConfig.stateOffAsStyleClass) { // if offStates are provided add OffStates
|
||||||
|
let offStatesArray = svgElementConfig.stateOffAsStyleClass.split(',')
|
||||||
|
for (const offState of offStatesArray) {
|
||||||
|
const elementClassInfo = offState.split(':')
|
||||||
|
const offStateElement = document.getElementById(elementClassInfo[0].trim())
|
||||||
|
if (offStateElement) {
|
||||||
|
offStateElement.classList.remove(elementClassInfo[1].trim())
|
||||||
|
} else {
|
||||||
|
console.warn(`Target element ${elementClassInfo[0].trim()} not found. Please check style stateOffAsStyleClass expression of ${element.id}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let onStatesArray = svgElementConfig.stateOnAsStyleClass.split(',')
|
||||||
|
for (const onState of onStatesArray) {
|
||||||
|
const elementClassInfo = onState.split(':')
|
||||||
|
const onStateElement = document.getElementById(elementClassInfo[0].trim())
|
||||||
|
if (onStateElement) {
|
||||||
|
onStateElement.classList.add(elementClassInfo[1].trim())
|
||||||
|
} else {
|
||||||
|
console.warn(`Target element ${elementClassInfo[0].trim()} not found. Please check style stateOnAsStyleClass expression of ${element.id}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (state === 'OFF') {
|
||||||
|
const updateColor = (stateOffColorRgbStyle) || ((element?.oldFill !== 'undefined') ? element?.oldFill : 'undefined')
|
||||||
|
if (updateColor !== 'undefined') {
|
||||||
|
element.style.fill = updateColor
|
||||||
|
}
|
||||||
|
if (svgElementConfig.stateAsOpacity) { // we use the flash element
|
||||||
|
let opacity = (svgElementConfig.invertStateOpacity) ? 1 : 0
|
||||||
|
opacity = (opacity < svgElementConfig.stateMinOpacity) ? svgElementConfig.stateMinOpacity : opacity
|
||||||
|
element.style.opacity = opacity
|
||||||
|
}
|
||||||
|
if (svgElementConfig.stateOnAsStyleClass) {
|
||||||
|
// remove OnState-Styles first
|
||||||
|
let onStatesArray = svgElementConfig.stateOnAsStyleClass.split(',')
|
||||||
|
for (const onState of onStatesArray) {
|
||||||
|
const elementClassInfo = onState.split(':')
|
||||||
|
const onStateElement = document.getElementById(elementClassInfo[0].trim())
|
||||||
|
if (onStateElement) {
|
||||||
|
onStateElement.classList.remove(elementClassInfo[1].trim())
|
||||||
|
} else {
|
||||||
|
console.warn(`Target element ${elementClassInfo[0].trim()} not found. Please check style stateOnAsStyleClass expression of ${element.id}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (svgElementConfig.stateOffAsStyleClass) { // if offStates are provided add OffStates
|
||||||
|
let offStatesArray = svgElementConfig.stateOffAsStyleClass.split(',')
|
||||||
|
for (const offState of offStatesArray) {
|
||||||
|
const elementClassInfo = offState.split(':')
|
||||||
|
const offStateElement = document.getElementById(elementClassInfo[0].trim())
|
||||||
|
if (offStateElement) {
|
||||||
|
offStateElement.classList.add(elementClassInfo[1].trim())
|
||||||
|
} else {
|
||||||
|
console.warn(`Target element ${elementClassInfo[0].trim()} not found. Please check style stateOffAsStyleClass expression of ${element.id}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else { // Percent, OpenClosed
|
||||||
|
if (svgElementConfig.stateAsOpacity && state) {
|
||||||
|
// we expect that number between 0 - 100
|
||||||
|
let opacity
|
||||||
|
if (stateType === 'OpenClosed') {
|
||||||
|
opacity = (state === 'OPEN') ? 1 : 0
|
||||||
|
} else if (stateType === 'Percent' && !isNaN(state)) {
|
||||||
|
opacity = parseFloat(state) / 100.0
|
||||||
|
}
|
||||||
|
opacity = (svgElementConfig.invertStateOpacity) ? 1 - opacity : opacity
|
||||||
|
opacity = (opacity < svgElementConfig.stateMinOpacity) ? svgElementConfig.stateMinOpacity : opacity
|
||||||
|
element.style.opacity = opacity
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
switch (stateType) {
|
switch (stateType) {
|
||||||
// currently no distinction is made regarding different state-types, only the following are supported (yet)
|
// currently no distinction is made regarding different state-types, only the following are supported (yet)
|
||||||
case 'OpenClosed':
|
case 'OpenClosed':
|
||||||
|
@ -300,92 +398,13 @@ export default {
|
||||||
case 'OnOff':
|
case 'OnOff':
|
||||||
const useProxy = tagName === 'g' && svgElementConfig.useProxyElementForState // if proxy should be used and element is of type group
|
const useProxy = tagName === 'g' && svgElementConfig.useProxyElementForState // if proxy should be used and element is of type group
|
||||||
const element = (useProxy) ? svgElement.querySelector('[flash]') : svgElement
|
const element = (useProxy) ? svgElement.querySelector('[flash]') : svgElement
|
||||||
if (!element) {
|
if (element) {
|
||||||
console.warn(`Element ${svgElement} is a group element but has no containing element with the attribute "flash"`)
|
processState(useProxy, element)
|
||||||
return
|
} else {
|
||||||
}
|
// let's try processing all paths within the group instead
|
||||||
if (state === 'ON' || stateType === 'HSB') {
|
const pathElements = svgElement.querySelectorAll('path')
|
||||||
if (useProxy && svgElementConfig.stateAsOpacity) { // we use the flash element
|
for (const path of pathElements) {
|
||||||
let opacity = (state === 'ON') ? 1 : 0
|
processState(useProxy, path)
|
||||||
opacity = (svgElementConfig.invertStateOpacity) ? 1 - opacity : opacity
|
|
||||||
opacity = (opacity < svgElementConfig.stateMinOpacity) ? svgElementConfig.stateMinOpacity : opacity
|
|
||||||
// TODO: use fill-opacity if fill not available
|
|
||||||
element.style.opacity = opacity
|
|
||||||
} else {
|
|
||||||
element.oldFill = element.style.fill
|
|
||||||
element.style.fill = stateOnColorRgbStyle
|
|
||||||
}
|
|
||||||
if (svgElementConfig.stateOnAsStyleClass) {
|
|
||||||
if (svgElementConfig.stateOffAsStyleClass) { // if offStates are provided add OffStates
|
|
||||||
let offStatesArray = svgElementConfig.stateOffAsStyleClass.split(',')
|
|
||||||
for (const offState of offStatesArray) {
|
|
||||||
const elementClassInfo = offState.split(':')
|
|
||||||
const offStateElement = document.getElementById(elementClassInfo[0].trim())
|
|
||||||
if (offStateElement) {
|
|
||||||
offStateElement.classList.remove(elementClassInfo[1].trim())
|
|
||||||
} else {
|
|
||||||
console.warn(`Target element ${elementClassInfo[0].trim()} not found. Please check style stateOffAsStyleClass expression of ${element.id}`)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let onStatesArray = svgElementConfig.stateOnAsStyleClass.split(',')
|
|
||||||
for (const onState of onStatesArray) {
|
|
||||||
const elementClassInfo = onState.split(':')
|
|
||||||
const onStateElement = document.getElementById(elementClassInfo[0].trim())
|
|
||||||
if (onStateElement) {
|
|
||||||
onStateElement.classList.add(elementClassInfo[1].trim())
|
|
||||||
} else {
|
|
||||||
console.warn(`Target element ${elementClassInfo[0].trim()} not found. Please check style stateOnAsStyleClass expression of ${element.id}`)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if (state === 'OFF') {
|
|
||||||
const updateColor = (stateOffColorRgbStyle) || ((element?.oldFill !== 'undefined') ? element?.oldFill : 'undefined')
|
|
||||||
if (updateColor !== 'undefined') {
|
|
||||||
element.style.fill = updateColor
|
|
||||||
}
|
|
||||||
if (svgElementConfig.stateAsOpacity) { // we use the flash element
|
|
||||||
let opacity = (svgElementConfig.invertStateOpacity) ? 1 : 0
|
|
||||||
opacity = (opacity < svgElementConfig.stateMinOpacity) ? svgElementConfig.stateMinOpacity : opacity
|
|
||||||
element.style.opacity = opacity
|
|
||||||
}
|
|
||||||
if (svgElementConfig.stateOnAsStyleClass) {
|
|
||||||
// remove OnState-Styles first
|
|
||||||
let onStatesArray = svgElementConfig.stateOnAsStyleClass.split(',')
|
|
||||||
for (const onState of onStatesArray) {
|
|
||||||
const elementClassInfo = onState.split(':')
|
|
||||||
const onStateElement = document.getElementById(elementClassInfo[0].trim())
|
|
||||||
if (onStateElement) {
|
|
||||||
onStateElement.classList.remove(elementClassInfo[1].trim())
|
|
||||||
} else {
|
|
||||||
console.warn(`Target element ${elementClassInfo[0].trim()} not found. Please check style stateOnAsStyleClass expression of ${element.id}`)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (svgElementConfig.stateOffAsStyleClass) { // if offStates are provided add OffStates
|
|
||||||
let offStatesArray = svgElementConfig.stateOffAsStyleClass.split(',')
|
|
||||||
for (const offState of offStatesArray) {
|
|
||||||
const elementClassInfo = offState.split(':')
|
|
||||||
const offStateElement = document.getElementById(elementClassInfo[0].trim())
|
|
||||||
if (offStateElement) {
|
|
||||||
offStateElement.classList.add(elementClassInfo[1].trim())
|
|
||||||
} else {
|
|
||||||
console.warn(`Target element ${elementClassInfo[0].trim()} not found. Please check style stateOffAsStyleClass expression of ${element.id}`)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else { // Percent, OpenClosed
|
|
||||||
if (svgElementConfig.stateAsOpacity && state) {
|
|
||||||
// we expect that number between 0 - 100
|
|
||||||
let opacity
|
|
||||||
if (stateType === 'OpenClosed') {
|
|
||||||
opacity = (state === 'OPEN') ? 1 : 0
|
|
||||||
} else if (stateType === 'Percent' && !isNaN(state)) {
|
|
||||||
opacity = parseFloat(state) / 100.0
|
|
||||||
}
|
|
||||||
opacity = (svgElementConfig.invertStateOpacity) ? 1 - opacity : opacity
|
|
||||||
opacity = (opacity < svgElementConfig.stateMinOpacity) ? svgElementConfig.stateMinOpacity : opacity
|
|
||||||
element.style.opacity = opacity
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
|
|
Loading…
Reference in New Issue