Setting the width of an image in a HTML5 template

Hello again,

I’m trying to work through my first HTML5 templates in Caspar.
I built a simple HTML5-template that has an image as background with a text on it.
I’m able to play out this template and to set the texts from my client.
So far so good.

Now I like to resize my image depending on the length of the text (controlled by the Caspar Client). So as far as I understood I have to set the img.width property of my HTML element. But how can I manage that?

To manipulate the text I send

Dim data As New CasparCGDataCollection
data.SetData(.Controls.Find("txt_" & i + 1, True).FirstOrDefault().Tag, .Controls.Find("txt_" & i + 1, True).FirstOrDefault().Text)
CasparDevice.Channels(1).CG.Add(cglayer, .cbx_template_folder.SelectedItem & "/" & .cbx_templates.SelectedItem, data)

But that doesn’t work for the width attribute. I tried:

data.SetData("img_oben.width", "100")
data.SetData("img_oben.width", 100)
data.SetData("img_oben/width", 100)
data.SetData("img_oben\width", 100)
data.SetData("img_oben width", 100)

and also

data.SetData("img_oben", ".width" & Cstr(100))

Is there any helping hand who has the right syntax or any hint for me?

Thanks a lot.

Ingo

Aren’t you better off setting that image as the background for the text’s container and setting image-size: cover in the css?

Hi rrebuffo,

Interesting remark. I’ll check that. Thank you.
However do you know how to set the width value via Caspar client?

Ingo

It totally depends on how your template is set up. That’s a template problem, not a client one.
Can you share your update() function?

Sure

/*
*   Data sent from CasparCG Client as templateData
*   is pushed into corresponding HTML id element
*
*   Usage:
*   insert a script reference in the HTML template header.
*   ex: <script type="text/javascript" src="CasparCG.js"></script>
*   Make sure that the id that you refer to is the innermost tag.
*   Everything within that tag id will be replaced by the value sent from CasparCG
*
*   put together by Tomas Linden
*   modified by Øjvind Søgaard Andersen
*
   Structure of data sent from CasparCG:
   <templateData>
      <componentData id="#idCaspar#">
         <data id="text" value="#valCaspar#" />
      </componentData>
      :
      :
      <componentData id="#idCaspar#">
         <data id="text" value="#valCaspar#" />
      </componentData>
   </templateData>
*/
// Global variable for data from CasparCG
var dataCaspar = {};

// Replace characters that could become a problem if left as is
function escapeHtml(unsafe) {
  return unsafe.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#039;");
}

// Parse templateData into an XML object
function parseCaspar(str) {
  var xmlDoc;
  if (window.DOMParser) {
    parser = new DOMParser();
    xmlDoc = parser.parseFromString(str, "text/xml");
  }
  dataCaspar = XML2JSON(xmlDoc.documentElement.childNodes);
}


// Make the XML templateData message into a more simple key:value object
function XML2JSON(node) {
  var data = {}; // resulting object
  for (k = 0; k < node.length; k++) {
    var idCaspar = node[k].getAttribute("id");
    var valCaspar = node[k].childNodes[0].getAttribute("value");
    if (idCaspar != undefined && valCaspar != undefined) {
      data[idCaspar] = valCaspar;
    };
  }
  return data;
}

// Main function to insert data
function dataInsert(dataCaspar) {
  for (var idCaspar in dataCaspar) {
    var idTemplate = document.getElementById(idCaspar);
    if (idTemplate != undefined) {
      idTemplate.innerHTML = escapeHtml(dataCaspar[idCaspar]);
    }
  }
}

// Call for a update of data from CasparCG client
function update(str) {
  parseCaspar(str); // Parse templateData into an XML object
  dataInsert(dataCaspar); // Insert data
}

// insert data from CasparCg client when activated
function play(str) {
  parseCaspar(str); // Parse templateData into an XML object
  dataInsert(dataCaspar); // Insert data
   gwd.actions.timeline.gotoAndPlay('document.body', 'start');
}

// Call for a next from CasparCG client
function next() {
  gwd.actions.timeline.play('document.body');
}

// Call for a stop from CasparCG client
function stop() {
  gwd.actions.timeline.gotoAndPlay('document.body', 'out');
}

// Call for a update and jump to preview (out)
function preview(str) {
  parseCaspar(str); // Parse templateData into an XML object
  dataInsert(dataCaspar); // Insert data
  gwd.actions.timeline.gotoAndPause('document.body', 'out');
}

// Main function to insert data
function dataInsert(dataCaspar) {
  for (var idCaspar in dataCaspar) {
    var idTarget;
    var property;
    if (idCaspar.indexOf('.')>0) {
      idTarget = idCaspar.split('.')[0];
      property = idCaspar.split('.')[1];
    } else {
      idTarget = idCaspar;
    }
    idTemplate = document.getElementById(idTarget);
    if (idTemplate != undefined) {
      if (property != undefined) {
        idTemplate.style[property] = dataCaspar[idCaspar];
      } else {
        idTemplate.innerHTML = escapeHtml(dataCaspar[idCaspar]);
      }
    }
  }
}

Try this. I can’t test it right now. You should be able to change the styles like id.property from the client.

Hi rrebuffo,

Doesn’t work in the first place.
But it gives me the idea how it works. :thinking:
I’m sure I’ll get it done.

Thanks for your help! :grinning:

Cheers
Ingo

As a guiding note: I would never send such information from the client. Because it makes the client too dependent on the template. Adapting the size of a background element to the text length can be done inside the template using javascript or some css tricks. To be able to set that from the client, it would need to know what font and size the template uses. And that should always be left to the template, so that you can use a different template, without changeant anything in the client (beside telling it to use the other template).

Are you sending the unit along with the value? like "1920px" or "100vw" instead of just "1920".

I agree with you, but sometimes the templates need to be a bit flexible. It doesn’t make sense to switch templates just to change the text’s color. If it’s done correctly and in a controlled way, templates should be more dynamic. A background width is not one of those cases though.

Dear didi, dear rrebuffo,

Or should I call you HTML-Gods?! :grinning:

Thanks rrebuffo for the hint with the unit. I obviously got the whole HTML idea wrong. Of course I haven’t sent the unit alongside the value. Doing this it works brilliantly. :+1:
That’s the problem if you only have my semi-knowledge.

And thank you didi for your remarks which I appreciate very much. Of course you’re right. Putting this logic or funtionality into the HTML itself should be the better way.
The challenge is that I don’t have the actual draft from our design department. So I can’t really work with the template. So I use my time to fiddle around with Caspar and HTML to get used to it and to check out what works for me and what doesn’t.

I’m looking forward to getting the actaul draft. Maybe I come back to you then to get it as dynamic as possible. Thanks. :grinning:

By the way:
This is a great forum. You always get an immediate and qualified answer to any request. Perfect help (not only) for beginners like me.

Cheers
Ingo

1 Like

hahaha. I an far away to be called like that. I was hit by the Flash EOL like a shock, around October last year and started with HTML template building beginning of this year. So I am still digging in the dark most of the time.

The good thing with JavaScript is, that it is very similar to ActionScript (Flash) so that at least the syntax is not completely new to me…

1 Like

Okay. Another question to the HTML-Gods :grinning:

I now got the draft from our design department. They did a pretty good job graphically but didn’t work with a timeline as I expected it.
They actually load an animation class (i.e. “animation_in”). For the out animation they remove the animation class from the document and load another class (“animation_out”). Pretty weird for me but works fine with two buttons.

My challenge is now to “press” these button from caspar.js script. And here ends my js knowledge.

 <script type="text/javascript">
    jQuery(document).ready(function() {

    	
    	jQuery('.start_animation').on('click', function(e){
    		e.preventDefault();
    		jQuery('body').removeClass('animation_in animation_out').addClass('animation_in');
    	})
    	
    	jQuery('.stop_animation').on('click', function(e){
    		e.preventDefault();
    		jQuery('body').addClass('animation_out');
    	})
    });
  </script>

Could anybody help me here?

I tried to put the following into the caspar.js

function play(str) {
  parseCaspar(str); // Parse templateData into an XML object
  dataInsert(dataCaspar); // Insert data
  jQuery('body').removeClass('animation_in animation_out').addClass('animation_in');
}

I also tried

   jQuery(document).ready(function() {
    		jQuery('body').removeClass('animation_in animation_out').addClass('animation_in');
    });
	

Both unsucessful. No error message but no function either.

I’m not quite sure if I can call functions in anothetr script like that. Could anybody help?

Thanks.
Ingo

Sometimes when doing CSS animations by swapping classes the browser cannot keep up and it helps to force a repaint - which in turn will trigger the CSS transitions.

See for instance this:

Thanks Tuomo,

I don’t quite get it.
You suggest to insert a dummy command into caspar.js to force the template to repaint?
Something like:

function play(str) {
  parseCaspar(str); // Parse templateData into an XML object
  dataInsert(dataCaspar); // Insert data
	jQuery('body').removeClass('animation_in animation_out').addClass('animation_in');
	var elem = document.scrollingElement;
}

I tried that but it doesn’t help.

I’m still not sure if my call for the jQuery function is correct.
Is it possible to use that call this way?

Ingo

Now that does not “do” anything yet.

The actual thing from the example is <some-element>.offsetHeight; -part. Hard to tell details, since we do not know what your parseCaspar(str) and dataInsert(dataCaspar) functions are doing and what your template HTML is like…

And there is jQuery involved, which… nevermind…:roll_eyes:

OK.
Unfortunately I can’t give you the whole template but here is at least the caspar.js I’m using:

/*
*   Data sent from CasparCG Client as templateData
*   is pushed into corresponding HTML id element
*
*   Usage:
*   insert a script reference in the HTML template header.
*   ex: <script type="text/javascript" src="CasparCG.js"></script>
*   Make sure that the id that you refer to is the innermost tag.
*   Everything within that tag id will be replaced by the value sent from CasparCG
*
*   put together by Tomas Linden
*   modified by Øjvind Søgaard Andersen
*
   Structure of data sent from CasparCG:
   <templateData>
      <componentData id="#idCaspar#">
         <data id="text" value="#valCaspar#" />
      </componentData>
      :
      :
      <componentData id="#idCaspar#">
         <data id="text" value="#valCaspar#" />
      </componentData>
   </templateData>
*/


// Global variable for data from CasparCG
var dataCaspar = {};

// Replace characters that could become a problem if left as is
function escapeHtml(unsafe) {
  return unsafe.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#039;");
}

// Parse templateData into an XML object
function parseCaspar(str) {
  var xmlDoc;
  if (window.DOMParser) {
    parser = new DOMParser();
    xmlDoc = parser.parseFromString(str, "text/xml");
  }
  dataCaspar = XML2JSON(xmlDoc.documentElement.childNodes);
}


// Make the XML templateData message into a more simple key:value object
function XML2JSON(node) {
  var data = {}; // resulting object
  for (k = 0; k < node.length; k++) {
    var idCaspar = node[k].getAttribute("id");
    var valCaspar = node[k].childNodes[0].getAttribute("value");
    if (idCaspar != undefined && valCaspar != undefined) {
      data[idCaspar] = valCaspar;
    };
  }
  return data;
}

// Main function to insert data
function dataInsert(dataCaspar) {
  for (var idCaspar in dataCaspar) {
    var idTemplate = document.getElementById(idCaspar);
    if (idTemplate != undefined) {
      idTemplate.innerHTML = escapeHtml(dataCaspar[idCaspar]);
    }
  }
}

// Call for a update of data from CasparCG client
function update(str) {
  parseCaspar(str); // Parse templateData into an XML object
  dataInsert(dataCaspar); // Insert data
}

// insert data from CasparCg client when activated
function play(str) {
  parseCaspar(str); // Parse templateData into an XML object
  dataInsert(dataCaspar); // Insert data
  jQuery('body').removeClass('animation_in animation_out').addClass('animation_in');
}

// Call for a next from CasparCG client
function next() {
 
}

// Call for a stop from CasparCG client
function stop() {
	jQuery('body').addClass('animation_out');
}

// Call for a update and jump to preview (out)
function preview(str) {
  parseCaspar(str); // Parse templateData into an XML object
  dataInsert(dataCaspar); // Insert data
}

And the part of the HTML that includes scripts

  
  <script type="text/javascript" charset="utf-8" src="jquery.min.js"></script>
  <script type="text/javascript">
    jQuery(document).ready(function() {
    	
    	jQuery('.start_animation').on('click', function(e){
    		e.preventDefault();
    		jQuery('body').removeClass('animation_in animation_out').addClass('animation_in');
    	})
    	
    	jQuery('.stop_animation').on('click', function(e){
    		e.preventDefault();
    		jQuery('body').addClass('animation_out');
    	})
    });
  </script>
  <script type="text/javascript" charset="utf-8" src="CasparCG.js"></script>
.
.
.
	<button class="start_animation" style="position: absolute; left: 30px; top: 30px; width: 100px; height: 30px;">START</button>
	<button class="stop_animation" style="position: absolute; left: 30px; top: 70px; width: 100px; height: 30px;">STOP</button>

The recent template contains two buttons that start and stop the animations. It’s done by loading the classes “animation_in” and “animation_out”.
All I try to do is call the “addClass” function from the caspar.js instead of the buttons.

Sorry I cannot get more precise but it’s copyright related material in the HTML.

Ingo

Ok, so…

CasparCG Client calls the play() function in the template when it’s started with the PLAY command (F2). This happens automatically.

To me, it looks like your template should already work. It has the play() function implemented and it is swapping the CSS classes… No need to do any further “button clicks” anywhere.

:thinking:

But it doesn’t.

The buttons are only for testing the template in the browser.
I’m aware that I don’t need them.

Is it necessary to include the jQuery script into the caspar.js as well or do they “know” each other via the HTML document?

Ingo

I try to avoid jQuery as corona…

I would do:

document.body.classList='animation_in'

But, that’s just me :slight_smile:

jQuery wasn’t my choice :grinning:

The template was delivered that way by the designer.

Sorry