Howdy, Stranger!

It looks like you're new here. If you want to get involved, click one of these buttons!

Stop Javascript from overriding/ignoring custom CSS (Harlowe)

Hello everyone,

I'm really close to my deadline for finishing my Twine story and I ran into an annoying issue with preloading images with Javascript. I got this handy piece of code from here.

function preloader() {
	if (document.images) {
		var img1 = new Image();
		var img2 = new Image();
		var img3 = new Image();

		img1.src = "http://domain.tld/path/to/image-001.gif";
		img2.src = "http://domain.tld/path/to/image-002.gif";
		img3.src = "http://domain.tld/path/to/image-003.gif";
	}
}
function addLoadEvent(func) {
	var oldonload = window.onload;
	if (typeof window.onload != 'function') {
		window.onload = func;
	} else {
		window.onload = function() {
			if (oldonload) {
				oldonload();
			}
			func();
		}
	}
}
addLoadEvent(preloader);

The issue with this is that although it now does preload the images fine, it seems to override/ignore the parts of my custom css that make some of my images appear slowly.

Being:
html.example
				{
		background-image: url("http://exampleurl.jpg";);
	background-repeat:no-repeat;
	background-size:contain;	
	background-position: center;
	background-color: Black;
	-webkit-animation: appear 2s;
	animation: appear 2s;
				}

They now just pop up instantly. Is there any way to solve this? Maybe by using the CSS way of preloading proposed by the website to work? (I can't get it to work.)
#preload-01 { background: url(http://domain.tld/image-01.png) no-repeat -9999px -9999px; }
#preload-02 { background: url(http://domain.tld/image-02.png) no-repeat -9999px -9999px; }
#preload-03 { background: url(http://domain.tld/image-03.png) no-repeat -9999px -9999px; }

Hope any of you can make sense of this. Thank in advance.

Comments

  • Pleas add a sample of html where the issue is actually occurring (like where you're using the ids and classes specified)
  • I'm not sure if this is what you mean, but a passage with that html.example tag looks like this:
    (set: ?passage to "
    <div class='container'></div>")
    (click: ?passage)[(goto: "nextpassage.")]|passage>[]
    

    which works of this bit of css in the stylesheet.
    .container {
    position: fixed;
    top: 0px;
    left: 0px;
    width: 100%;
    height: 100%;
    }
    

    But that's just to make the image clickable (for going to the next passage). To be clear. Everything works with and without the javascript, but without the images have to load in first (which looks ugly) and with the images don't fade in anymore.
  • @Silvanoshei

    You can't use a window.onload event to run code in a Story Format because that event occurs before your custom Javascript gets parsed and executed.

    I don't recognize the appear attribute of your animation: appear 2s; and I could not find it in the MDN animation documentation.
  • edited June 2016
    I'm still not sure how the preloading of images could cause them to ignore CSS; can you reproduce the issue in multiple browsers? Which browser are you using?
  • kgroat wrote: »
    I'm still not sure how the preloading of images could cause them to ignore CSS; can you reproduce the issue in multiple browsers? Which browser are you using?

    Tried 3 different webbrowsers:
    Firefox [the one I used initially]: loads at the start and then doesn't fade in images
    Chrome: doesn't seem to apply the Javascript at all
    Edge: the images are black for ages and then just pop up (when they're done loading I assume)
    greyelf wrote: »
    You can't use a window.onload event to run code in a Story Format because that event occurs before your custom Javascript gets parsed and executed.

    So yeah it's probably the window.onload thing messing it up.
    greyelf wrote: »
    I don't recognize the appear attribute of your animation: appear 2s; and I could not find it in the MDN animation documentation.

    I think I found animation: appear on a Twine-unrelated website somewhere. Funnily enough it was the only fade in code that worked for me inside html.example.

    Maybe I should rephrase my question. How would you guys stop images from rendering in front of your eyes? I can see two options: preloading at the start of the story or showing something while it loads. Would prefer preloading. But how to go about this?
  • @kgroat:
    The first problem is that the preloader function is never called so no images are actually being pre-loaded, this is because the addLoadEvent function is attaching to the window.onload event after the event has already fired.

    Timing/Delay based CSS could appear to be ignored if there is an added delay of the image having to be downloaded when it is needed.

    eg. CSS says delay the visualizing of the image for two second, but it takes an additional x amount of time to download the image first, so the delay ends up being longer than two seconds.

    I believe that there is also an issue with the animation: appear 2s; CSS.
  • edited June 2016
    @Silvanoshei
    Do any of the images need to be displayed within the first Passage show to the Reader? or as the background of the story?
  • edited June 2016
    The first passage is an image yes. But I could make it something else and then refer it to the first passage after a set time has passed. If it helps to have something in front of it.

    Also have a startup passage with (set: $save to false) in it.
  • edited June 2016
    Harlowe does not have an Initialising event like SugarCube does but you could fake one:

    1. Add a new "Please Wait" passage to your story, display some nice message (with NO links) asking the Reader to wait while things are being set up. Make this new passage the Starting Point of your story.

    2. Add code in your Story Javascript area that uses a setTimeout() function to call your preloader function. That way your images get preloaded while the "Please Wait" passage is being shown to the Reader.

    3. Add a (live:) macro to your "Please Wait" passage that checks if the pre-loading of the images has finished and when it has use a (goto:) macro to send the Reader to the original start of the story. You may be able to use the size of the window.images collection as a way to determine if all your images have been loaded.
  • edited June 2016
    Thanks for all the help :) Still have some questions:

    1. So you mean that I should set a timeout function to delay the javascript by, say, 1 second? So replace the preloader with that like I did below? Or am I misinterpreting?
    var timeoutID;
    
    function delayedAlert() {
      timeoutID = window.setTimeout(slowAlert, 1000);
    }
    
    function slowAlert() {
      if (document.images) {
    		var img1 = new Image();
    		var img2 = new Image();
    		var img3 = new Image();
    
    		img1.src = "http://domain.tld/path/to/image-001.gif";
    		img2.src = "http://domain.tld/path/to/image-002.gif";
    		img3.src = "http://domain.tld/path/to/image-003.gif";
    	}
    }
    function addLoadEvent(func) {
    	var oldonload = window.onload;
    	if (typeof window.onload != 'function') {
    		window.onload = func;
    	} else {
    		window.onload = function() {
    			if (oldonload) {
    				oldonload();
    			}
    			func();
    		}
    	}
    }
    

    2. How would I make it check the size of the window.images collection and continue if a certain size is reached?
  • @greyelf

    Also (sorry, no experience with this whatsoever) what's the html needed to invoke that function in the first passage? Or does it invoke itself?
  • Sorry, I had to sleep.

    note: A more experienced Javasccript coder may find issues with the following solution but it is a starting point.

    I would start with Javascript like the following which creates a global namespace to contain your custom code, it uses a variable to track how many images have been preloaded so far, and it includes a function that does the actual pre-loading.
    // Create a namespace for storing your custom code.
    if (! window.Me) {
    	window.Me = {
    		preloadedImages: 0,
    		preloadImages: function(){
    			// Your code for preloading the images goes here.
    			
    			// eg.
    			var img = new Image();
    			img.src = "https://upload.wikimedia.org/wikipedia/commons/thumb/8/85/Smiley.svg/2000px-Smiley.svg.png";
    			img.onload = function() {
    				Me.preloadedImages++;
    			};
    
    			img = new Image();
    			img.src = "https://s-media-cache-ak0.pinimg.com/736x/51/aa/c8/51aac8e5328790194fc4220dfd88c1f7.jpg";
    			img.onload = function() {
    				Me.preloadedImages++;
    			};
    		}
    	}
    }
    
    setTimeout(Me.preloadImages, 500);
    

    Next you need a Please Wait passage to show to the Reader, it needs to be assigned the Starting Point of your story, it needs to contain your "wait" message, and it needs to check if your images have finished pre-loading and if so move the Reader to your Start passage
    Some nice ''Initializing story, please wait...'' message.
    
    (live: 2s)[
    	(if: Me's "preloadedImages" is 2)[
    		(goto: "Start")
    	]
    ]
    

    WARNING: Harlowe currently supports accessing an element of a global object using the object's property syntax, there is no way of knowing if this will be the case in future versions of the story format.
  • @greyelf

    How dare you sleep. The world needs you. :)

    Anyway, you're an absolute lifesaver. It works! I understand this solution may not be supported in future versions, but as long as it works now (publishing it tomorrow) I'm satisfied.

    Remains the question: how do I now fade in those images?

    Since my way is now broken.
    -webkit-animation: appear 2s;
    	animation: appear 2s;
    

    Is there a way to do it within a passage? Or in the css html?
    html.example
    				{
    		background-image: url("example.jpg");
    	background-repeat:no-repeat;
    	background-size:contain;	
    	background-position: center;
    	background-color: Black;
    				}
    

    I tried so many solutions before that didn't work for some reason. I now don't even know where to start looking.
  • note: a more experienced CSS coder may have a better answer.

    A common way to implement Fade-In is to change the opacity property of an element from 0 to 1 using some sort of trigger combined with a transition.

    Unfortunately as far as I know there is no direct way to change the opacity of a background image, you generally have to change the opacity of the element the background image is being attached to but in your case that is the html element which would effect the opacity of the whole document.

    The closest answer I could find was Fade in background image on load[stackoverflow] which uses Javascript to setup the background image during a onload event using jQuery's css() and fadeIn() functions. What I could not work out is how to combine that technique with conditional tag based styling without having to embed/hard-wire the code into your header tagged passage.
  • @greyelf

    Is the issue it being a background image? Because there is no need for it to be in the background per se. There is nothing else in the passage.
  • edited June 2016
    So, I found a workaround just putting the image in the passage. Like this:
    (live:1s)[
    (transition: "dissolve")[
    <img src='img.jpg' style='height:100%;position:fixed;top:0px;left:0px;'>
    ](stop:)
    ]
    

    While this works brilliantly, I've now got a slight issue which I thought would be solved in an instant, namely that the image is not centred, but I've been googling for ages, putting all kinds of code in (instead of "top:0px;left:0px") and nothing seems to work.
  • Instead of adding the styling directly to the img element I suggest using a class (like "background") and doing the styling in the Story Stylesheet area (where it belongs).

    So the contents of the passage would look something like: (excluding your macros)
    {<img src="image.jpg" class="background">
    
    }The rest of the passage's contents.....
    
    ... and the CSS in the Story Stylesheet area would look something like
    img.background {
    	min-height: 100%;
    	width: 100%;
    	height: auto;
    	position: fixed;
    	top: 0;
    	left: 0;
    	z-index: -1;
    }
    
  • edited June 2016
    Edit: nevermind, fixed it myself. A good night sleep can do wonders.
    img.background {
    	height: 100%;
    	width: auto;
    	z-index: -1;
    	position: fixed;
    	top: 50%;
    	left: 50%;
    	margin-right: -50%;
    	transform: translate(-50%, -50%);
    }
    
Sign In or Register to comment.