openui5
openui5 copied to clipboard
sap.m.Image src=*.svg support
Hi guys, i am currently using svg images as Image sources.
To be able to apply styling via css the external svg needs to be converted as inline svg.
Currently this is a work in progress and i do something like
jQuery('.svg-inject .sapMImg').each(function(){
var $img = jQuery(this);
var imgID = $img.attr("id");
var imgDataSapUi = $img.attr("data-sap-ui");
var imgRole = $img.attr("role");
var imgAriaHidden = $img.attr("aria-hidden");
var imgClass = $img.attr("class");
var imgStyle = $img.attr("style");
var imgURL = $img.attr("src");
jQuery.get(imgURL, function(data) {
// Get the SVG tag, ignore the rest
var $svg = jQuery(data).find("svg");
// Add replaced image's ID to the new SVG
if(typeof imgID !== "undefined") {
$svg = $svg.attr("id", imgID);
}
if(typeof imgDataSapUi !== "undefined") {
$svg = $svg.attr("data-sap-ui", imgDataSapUi);
}
if(typeof imgRole !== "undefined") {
$svg = $svg.attr("role", imgRole);
}
if(typeof imgAriaHidden !== "undefined") {
$svg = $svg.attr("aria-hidden", imgAriaHidden);
}
// Add replaced image's classes to the new SVG
if(typeof imgClass !== "undefined") {
$svg = $svg.attr("class", imgClass + " svg-inline");
}
// Add replaced image's styles to the new SVG
if(typeof imgStyle !== "undefined") {
$svg = $svg.attr("style", imgStyle);
}
// Remove any invalid XML tags as per http://validator.w3.org
$svg = $svg.removeAttr("xmlns:a");
// Check if the viewport is set, if the viewport is not set the SVG wont't scale.
if(!$svg.attr('viewBox') && $svg.attr('height') && $svg.attr('width')) {
$svg.attr('viewBox', '0 0 ' + $svg.attr('height') + ' ' + $svg.attr('width'))
}
// Replace image with new SVG
$img.replaceWith($svg);
}, 'xml');
There are also more experienced libs like https://github.com/robbue/jquery.svgInject availbale, which uses an inline cache for replacing img with svg.
This is only a WIP but still works. I just started to thing (and learn) about using SVG. Using it this way, the image will be replaced by an inline svg object.
You also need densityAware = false because svg is a vector and src should not load @2.svg
It would be great, if such feature could be integrated into the sap.m.image, maybe with an inline option by default to be able to use such SVGs also in the sap.m.ObjectHeader.
We use this feature inside out theme for company logo's, where the external src is ok.
Using SVGs as user placeholder images we like to control the fills, strokes, etc with colors inside our custom specific theme using $BaseColor, $AccentColor and such SVGs needs to be replaced as inline object svg.
What do you thing?
Regards, Holger
I played around with a custom widget
sap.ui.define([
"sap/m/Image",
"3rd/jquery.svgInject"
],
function (Image) {
"use strict";
var SvgImage = Image.extend("uniorg.m.SvgImage", {
renderer: {}
});
/**
* After src change replace img.src pointing to external *.svg with embedded svg using custom version of svgInject
* current issue : changing src by setSrc -> need to rerender svg to embedd changed markup!!!
* src property binding will cause an rerender overhead (by design)!
* @private
*/
SvgImage.prototype._updateDomSrc = function() {
if (Image.prototype._updateDomSrc) {
Image.prototype._updateDomSrc.apply(this, arguments);
}
var $DomNode = this.$();
if ($DomNode.length) {
jQuery($DomNode).svgInject(function() {
// Injection complete
});
}
};
return SvgImage;
},
/*bExport*/ true
);
But this approach will not work with Images inside containers, ex. ObjectHeader using icon.
From my opinion, SVG would be a nice benefit for images/icons in general, because in situations where you need more than one icon color, you can use SVG and control any portion using custom css.
The above custom control works quite well, but i need to solve all kind of rerendering issues if changing src, etc. I am also using a modified version of svgInject to internally cache loaded SVGs, but this are only poor workarounds since i miss such a functionality inside core ;-(
Regards, Holger
Here is the modified svgInject
/*
svgInject - v1.0.0
jQuery plugin for replacing img-tags with SVG content
by Robert Bue (@robert_bue)
Dual licensed under MIT and GPL.
*/
;(function($, window, document, undefined) {
var pluginName = 'svgInject';
/**
* Cache that helps to reduce requests
*/
function Cache(){
this._entries = {};
}
/**
* Cache prototype extension for inheritance
* @type {Object}
*/
Cache.prototype = {
/**
* Checks if there is already an entry with that URL
* @param {String} url
* @return {Boolean}
*/
isFileInCache : function( url ){
return (typeof this._entries[url] !== 'undefined');
},
/**
* Returns the entry of the given url if it exists. If not, false is returned;
* @param {String} url
* @return {Object||Boolean} Entry or false if there is no entry with that url
*/
getEntry : function( url ){
return this._entries[url] || false;
},
/**
* Adds an entry to the cache
* @param {String} url
* @param {Function} callback
*/
addEntry : function( url, callback ){
// check if the given callback is a function
var isCallbackFunction = (typeof callback === 'function');
// check if the entry is already in the cache
if(this._entries[url]){
// if the callback is a function and the data is loaded, we execute it instantly with the cached data
if(isCallbackFunction && !this._entries[url].isLoading){
callback(this._entries[url].data);
}else if(isCallbackFunction){ // if the callback is a function and the data is still loading, we push in into the callback array
this._entries[url].callbacks.push(callback);
}
return this._entries[url];
}else{
var callbacks = [];
if(isCallbackFunction){
callbacks.push(callback);
}
// put the entry into the cache
this._entries[url] = {
isLoading : true,
callbacks : callbacks
};
}
},
/**
* Updates the entry after the data is loaded and executes all the callbacks for that entry
* @param {String} url
* @param {*} data
*/
addEntryData : function( url, data ){
var entry = this._entries[url];
if(typeof entry !== 'undefined'){
entry.data = data;
entry.isLoading = false;
this.executeEntryCallbacks(url);
}
},
/**
* Executes all callback for the entry with the given url
* @param {String} url
*/
executeEntryCallbacks : function( url ){
var entry = this._entries[url];
if(typeof entry !== 'undefined'){
for(var i = 0, j = entry.callbacks.length; i < j; i++){
entry.callbacks.shift()(entry.data);
i--;
j--;
}
}
}
};
function Plugin(element, options) {
this.element = element;
this.$element = $(element);
this.callback = options;
this._name = pluginName;
this._cache = Plugin._cache;
this.init();
}
Plugin._cache = new Cache();
Plugin.prototype = {
init: function() {
//this.$element.css('visibility', 'hidden');
this.injectSVG(this.$element, this.callback);
},
injectSVG: function( $el, callback) {
var imgURL = $el.attr('src');
var imgID = $el.attr('id');
var imgClass = $el.attr('class');
var imgStyle = $el.attr('style');
var imgData = $el.clone(true).data();
var dimensions = {
w: $el.attr('width'),
h: $el.attr('height')
};
var _this = this;
// If the file is not in the cache, we request it
if(!this._cache.isFileInCache(imgURL)){
$.get(imgURL, function(data) {
var svg = $(data).find('svg');
// File is put into the cache
_this._cache.addEntryData( imgURL, svg );
});
}
// We add an entry to the cache with the given image url
this._cache.addEntry(imgURL , function( svg ){
// When the entry is loaded, the image gets replaces by the clone of the loaded svg
_this.replaceIMGWithSVG($el, svg.clone(), imgID, imgClass, imgStyle, imgURL, imgData, dimensions, callback);
});
},
replaceIMGWithSVG : function( $el, svg, imgID, imgClass, imgStyle, imgURL, imgData, dimensions, callback ){
if (typeof imgID !== undefined) {
svg = svg.attr('id', imgID);
}
if (typeof imgClass !== undefined) {
var cls = (svg.attr('class') !== undefined) ? svg.attr('class') : '';
svg = svg.attr('class', imgClass + ' ' + cls + ' replaced-svg');
}
if (typeof imgStyle !== undefined) {
svg = svg.attr('style', imgStyle);
}
if (typeof imgURL !== undefined) {
svg = svg.attr('data-url', imgURL);
}
$.each(imgData, function(name, value) {
svg[0].setAttribute('data-' + name, value);
});
svg = svg.removeAttr('xmlns:a');
var ow = parseFloat(svg.attr('width'));
var oh = parseFloat(svg.attr('height'));
if (dimensions.w && dimensions.h) {
$(svg).attr('width', dimensions.w);
$(svg).attr('height', dimensions.h);
} else if (dimensions.w) {
$(svg).attr('width', dimensions.w);
$(svg).attr('height', (oh / ow) * dimensions.w);
} else if (dimensions.h) {
$(svg).attr('height', dimensions.h);
$(svg).attr('width', (ow / oh) * dimensions.h);
}
$el.replaceWith(svg);
var js = new Function(svg.find('script').text());
js();
if ( typeof callback === 'function' ) {
callback();
}
}
};
$.fn[pluginName] = function(options) {
return this.each(function() {
if (!$.data(this, 'plugin_' + pluginName)) {
$.data(this, 'plugin_' + pluginName, new Plugin(this, options));
}
});
};
})(jQuery, window, document);
Thanks! I've forwarded this feature request to the responsible product owner.
Hi @matz3 what's the status on this task ?.
Thanks, Ted.
In the meantime, i have created a custom control and also a set of prototypes to support this in our tools!
See my blog http://openui5.blogspot.com/2017/01/svgimage.html
I think, the whole UI5 lib would benefit from SVG support (and i would be more stable accross releases ;-)
Inside my blog Demo, i had to fix some CSS classes to use images instead icons accross the widgets. Mostly padding/margin issues, because no one seems to test the usage of images instead of icons at the possible places ;-)
Widthout the fixes, the UI looks a little bit ugly.
/* sap.m.Button css fix */
.sapMBtnIcon.uiSVGImage {
padding: 0.25rem;
box-sizing: border-box;
height: 100%;
}
/* sap.m.MessageStrip custom icon fix */
.sapMMsgStripIcon>svg {
width: auto;
height: 1rem;
}
/* sap.ui.unified fix */
.sapUiMnuItmIco>svg {
padding-left: 0;
max-width: 1rem;
max-height: 1rem;
}
The fixes are also needed for the usage of regular images instead of icons!
Regards Holger
@tdniksfsap1 No update, yet. I've opened an internal ticket (1780100846) to clarify if this is something we would see as a new feature for sap.m.Image or not.
Hi Holger, this is very interesting functionality about the Image and changing themes. We haven't had such a requirement till now, but I would take the proposal to our PO and Architect for discussion and implementing a possible feature.
Great Thanks, Mihail.
I need this aswell. Especially because I want to access the path of an svg images. I need the image rendered as an <object> because I have to access contentDocument.
This enhancement will be covered in BLI FIORITECHE1-1513. Any progress will be posted here.
Hello, The request has been prioritized to be addressed in one of the coming two takts. I will add updates here. Best regards, Jordan
Hello @hschaefer123,
A bit of clarification needed.
Do I understand you correctly that the current possibility of displaying an SVG file inside image by setting a path to the src property of Image control is not entirely enough?
If yes, do you mean that the Image control needs to make it easier to reference SVG files via the src property but instead of passing this internally to an IMG tag, rather fetch and display the SVG inline so you can access the DOM and manipulate it with CSS?
In the light of this, @ManuelB can you provide a bit more information on your use case?
Thanks and regards, Jordan
@jdichev we used an svg map from germany with all federal states. They were clickable.
Hi @jdichev, you are right! We need exactly the svg inlined like other tools like vue-inline-svg doing also.
The main reason is to be able to apply the current theme colors to svg images. For example, an avatar svg, that can have multiple color layers, that can be themed with plain style like
#svgIdLayer1 { fillcolor: var (--sapBrandColor); }
Like in the concept off IllustrationMessages, you would be able to use SVG images that adapt custom theming on-the-fly.
We also do things like @ManuelB to be able to use/generate ExplorationImageSVG files to be able to generate technical pictures with spareparts that have clickable regions to graphically drill down into BOMs.
Best regards Holger
Hello @hschaefer123,
Thanks for the clarification. Additionally, I need to ask how do you intend to manage the CSS for such cases as this is something that should hand in hand - the SVG and the respective CSS?
@IlianaB FYI
Best regards, Jordan
ping @hschaefer123 for clarification
Hi, since we do not have controls that include css, generally the svg will contain custom css classes or ids.
For that reason, i will use a custom.css file added to the manifest.
With vue for example, this is easier with scoped css, but this is out-of-scope for ui5.
Best regards Holger
This issue was covered in backlog BGSOFUIPIRIN-5679 and implemented with https://github.com/SAP/openui5/commit/d459de2f3a44e76d7f608cf58b10cfd646185c91.