animating a circle with Canvas - how to expand to fill entire viewport?
Asked Answered
B

2

7

Codepen

http://codepen.io/tconroy/pen/RPzxgz

Basic setup:

I am trying to create a page structured with 2 columns inside of a centered container, max 1600px wide.

The left column contains the page content. The right column contains an ad unit (say, 640x480 px wide).

At 768px or lower media breakpoint, the 2 columns should stack ( so that the content is on top, and the ad is below it ).

The problem

When the page loads, there should be a 400x400px canvas element containing a white circle in the center of the screen (absolute center -- vertical and horizontal).

The Circle animates to a position directly behind the left column content.

After this, the circle should "expand" to fill the entire user's viewport, without covering the content or causing scrollbars to appear.

As shown in my below fiddle, I have gotten the initial circle / movement animation to work, however I am running into issues trying to figure out the expand portion. I need the circle to appear to grow/expand until it covers the entire viewport, but all text content / ad unit should not be obscured and no scrollbars should appear as a result of the animation.

I'm incredibly unfamiliar with canvas, so if anyone could give me a hand with without breaking the initial animation this it would be much appreciated. :)

http://codepen.io/tconroy/pen/RPzxgz

HTML:

<div class="container">
  <div class="navigation row">
    <h1 style="float:right;"><a href="#">Continue</a></h1>
  </div>
  <div class="content row">
    <div class="circle-wrap">

      <canvas class="circle" width="400" height="400"></canvas>

      <div class="template-output">
        <h1 class="title">
              <span class="upper">Title TopLine</span>
              <span class="lower">Title Bottom</span>
            </h1>
        <div class="body">
          <p class="description">Lorem ipsum dolor sit amet, consectetur adipisicing elit. Dolorum distinctio nesciunt nostrum magni neque. Iusto et delectus iure distinctio cupiditate, a sint doloremque ratione saepe sunt quisquam assumenda, eaque velit?</p>
          <p class="byline">- Author Name</p>
        </div>
      </div>

    </div>
  </div>


  <div class="ads row">
    <figure class="ad">
      <figcaption>Advertisement</figcaption>
      <div id="welcome"></div>
    </figure>
  </div>


</div>
</div>

CSS:

/* BASE STYLES */
* {
  box-sizing: border-box;
  padding: 0;
  margin: 0;
}

html, body {
  width: 100%;
  height: 100%;
  background: gray;
  overflow: hidden;
}

/* END BASE STYLES */
/* LAYOUT */
.container {
  width: 100%;
  height: 100%;
  max-width: 1600px;
  margin: 0 auto;
  outline: 1px solid black;
}

.row {
  width: 50%;
  height: 100%;
  float: left;
  display: block;
}
.row.content {
  border: 1px solid blue;
}
.row.ads {
  border: 1px solid red;
}
.row.navigation {
  width: 100%;
  height: auto;
}
@media screen and (max-width: 768px) {
  .row {
    width: 100%;
    height: auto;
  }
}

/* END LAYOUT STYLES */
/* ADVERTISEMENT */
.ad {
  display: table;
  margin: 0 auto;
}
.ad figcaption {
  text-align: center;
  margin: 0 auto;
}
.ad #welcome {
  outline: 1px solid black;
  background: darkgray;
  width: 640px;
  height: 480px;
  margin: 0 auto;
}

/* END ADVERTISEMENT STYLES */
/* CONTENT */
.content {
  min-height: 400px;
}
.content .template-output {
  width: 400px;
  position: relative;
}
.content .template-output .title {
  font-size: 4em;
}
.content .template-output .title span {
  display: block;
  text-transform: uppercase;
}
.content .template-output .title .upper {
  text-align: left;
}
.content .template-output .title .lower {
  text-align: right;
}
.content .template-output .body {
  position: relative;
}
.content .circle-wrap {
  position: relative;
  width: 100%;
  height: 100%;
  margin: 0 auto;
}
.content .circle-wrap .circle {
  width: 400px;
  height: 400px;
  outline: 1px solid black;
  position: absolute;
  left: 0;
  top: 0;
  transform: translate(0%, 0%);
}
.content .circle-2 {
  position: absolute;
  width: 100%;
  height: 100%;
  left: 0;
  top: 0;
}

/* END CONTENT STYLES */

JAVASCRIPT (included: jQuery and GSAP TweenMax libraries)

/*
  I have successfully created the canvas element in the center 
  of the screen and animate it to the left column. Now, I want the circle to "expand" behind the content, and fill the entire viewport.
  It should not cause scrollbars to appear or cover the text content.
*/
(function () {

    var tl = new TimelineMax();

    var $canvas = $('.circle'),
        $canvas_wrap = $('.circle-wrap'),
        canvas = $canvas[0],
        context = canvas.getContext('2d'),
        canvas_width = canvas.width,
        canvas_height = canvas.height;
        context.globalCompositeOperation='destination-over';
    draw(context);


    /* TIMELINE STUFF */
    var canvas_opts = {
        'center-to-destination': {
            xPercent: -50,
            yPercent: -50,
            top: '50%',
            left: '100%',
            delay: 1.5
        }
    };
    tl.from(canvas, 1, canvas_opts['center-to-destination']);


})();
Baggott answered 20/8, 2015 at 23:17 Comment(21)
do you want the whole canvas object to stretch and cover the entire viewport or do you want the inner circle that you have drawn to do that? two different things imho. also, whichever of these two objects you choose to animate, setting them behind all of the content would mean playing with the z-index CSS property of not just the canvas object, but also the other objects that are supposed to appear on top of it. I think your HTML structure will need to change a lot for that. I could be wrong of course.Oeuvre
is this anywhere close to what you are looking for?Oeuvre
The later is what I'm trying to do (use the inner circle to fill the viewport)Baggott
you had a look at the demo I created?Oeuvre
@TahirAhmed yes -- it's not quite what I want. The circle looks pixelated on the scale up, but it should look as if the circle is just expanding from its center to fill the entire veiwport. Something like this: codepen.io/johnheiner/pen/WvOMzJ but the inverse, with the effect coming from the circle outwards (and not covering up the text content).Baggott
I see. So that animation that happens which starts to go towards the left side, we may have to remove that. You are fine with that? Because that part is included in your codepen demo.Oeuvre
That animation needs to exist, but it can be changed/reworked. Any of the markup or code can be changed but a version of that must be in the final piece :)Baggott
Ok. how about this?Oeuvre
@TahirAhmed yes just like that, it's just missing the first "to the left" animation.Baggott
ok so it needs to go left and expand from there? rather than from center?Oeuvre
@TahirAhmed correct -- to the left (beind the text) then expandBaggott
also, can it straight away start from left? or does it have to start from center, move to left, and then continue expanding from left?Oeuvre
start from center, move to left, and then continue expanding from left :)Baggott
refresh the same link I provided above.Oeuvre
Pretty good, but the circle is overlapping the text content on the right side of the page. It should be "behind" all other content.Baggott
Also, the move-left animation isn't responsive -- the content will not always be 200px from the left but rather a percentage.Baggott
refresh the same. regarding the move-left animation, the 200 for currX is basically dependant on currRadius. Whatever amount you initially animate currRadius with, you have to provide the same amount to the next currX animation.Oeuvre
you made the container absolute so now it's not centered. There's not going to be a consistent pixel value for the left animation to be offset by since it's a responsive page.Baggott
I like your solution, the "left" animation just needs to position itself percentage-based instead of pixel-based so it can be responsive.Baggott
I am unable to understand your concern over the left animation. Can you tell me a example scenario where it behave differently? Please edit the codepen demo in order to demonstrate if you need to.Oeuvre
Actually, I seem to have gotten the desired behavior after I've modified your code. I'm going to wait a couple days to see if any better solutions are posted, but if not please post a question so I can give you bounty :-)Baggott
O
2

This is what I have been able to produce based on my understanding, I could be wrong.

Here is what has changed from your code:

  • canvas HTML element has been promoted and moved out, put just before opening of container HTML element.
  • Both canvas and container have been given a position: absolute with z-index values of 0 and 1 respectively, through the use of .set() methods of TweenMax in JavaScript.
  • canvas's width and height are also set to innerWidth and innerHeight of window object respectively.
  • A continuous running render method is created which is hooked into tick event which is fired by ticker object present inside TweenLite object.
  • Initial properties for the animation are set in canvasProps object.
  • This canvasProps object is then animated using three calls to .to() method of TweenMax.
  • Finally, the render method keeps refreshing the canvas and draws a circle based on the continuously changing values found within canvasProps method.

JavaScript:

(function() {
  var container = $('.container');
  var canvas = $('.circle')[0];
  var context = canvas.getContext('2d');
  canvas.width = window.innerWidth;
  canvas.height = window.innerHeight;

  TweenMax.set(container, { position: 'absolute', zIndex: 1 });
  TweenMax.set(canvas, { position: 'absolute', zIndex: 0 });

  var canvasProps = {
    currX: window.innerWidth * 0.5,
    currY: window.innerHeight * 0.5,
    currRadius: 0
  };

  TweenLite.ticker.addEventListener('tick', render, false);
  TweenMax.to(canvasProps, 1, { currRadius: 200, ease: Expo.easeInOut });
  TweenMax.to(canvasProps, 1, { delay: 1, currX: 200, ease: Expo.easeInOut });
  TweenMax.to(canvasProps, 1, { delay: 2, currRadius: window.innerWidth, ease: Expo.easeInOut });

  function render() {
    context.clearRect(0, 0, window.innerWidth, window.innerHeight);
    context.beginPath();
    context.arc(canvasProps.currX, canvasProps.currY, canvasProps.currRadius, 0, Math.PI * 2, true);
    context.fillStyle = 'white';
    context.fill();
  }
})();

Hope this helps.

Oeuvre answered 25/8, 2015 at 2:59 Comment(0)
S
0

If you extend the section where you have your canvas options and t1.from, you can accomplish what you're seeking (I did add a "var" to your canvas definition so that I could access the variable later):

var newWidth = $('#left_column').width();
var growBy = newWidth/canvas_width;

var grow_opts = {
    'grow': {
        scale: growby,
        transformOrigin "0px 0px",
        delay: 1.5
    }
};

tl.to(canvas, 1, grow_opts['grow']);

gist with fork of your codepen code here: https://gist.github.com/rddill-IBM/1b92bf6c03d427259a06

Spaceport answered 26/8, 2015 at 20:45 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.