Pen Settings

HTML

CSS

CSS Base

Vendor Prefixing

Add External Stylesheets/Pens

Any URL's added here will be added as <link>s in order, and before the CSS in the editor. You can use the CSS from another Pen by using it's URL and the proper URL extention.

+ add another resource

JavaScript

Babel includes JSX processing.

Add External Scripts/Pens

Any URL's added here will be added as <script>s in order, and run before the JavaScript in the editor. You can use the URL of any other Pen and it will include the JavaScript from that Pen.

+ add another resource

Packages

Add Packages

Search for and use JavaScript packages from npm here. By selecting a package, an import statement will be added to the top of the JavaScript editor for this package.

Behavior

Save Automatically?

If active, Pens will autosave every 30 seconds after being saved once.

Auto-Updating Preview

If enabled, the preview panel updates automatically as you code. If disabled, use the "Run" button to update.

Format on Save

If enabled, your code will be formatted when you actively save your Pen. Note: your code becomes un-folded during formatting.

Editor Settings

Code Indentation

Want to change your Syntax Highlighting theme, Fonts and more?

Visit your global Editor Settings.

HTML

              
                <div id="canvasContainer"></div>
<!-- this is only visible in "Full Page" view  -->
<span id="textInputSpan">
  Enter your name (max 7 chars) :
  <input id="textInput" maxlength="7" type="text" width="150" />
  <button onclick="changeText()">GO!</button>
</span>
              
            
!

CSS

              
                html, body{
  margin : 0px;
  width : 100%;
  height : 100%;
  overflow: hidden;
}

#canvasContainer{
  margin : 0px;
  width : 100%;
  height : 100%;
  position: absolute;
}

#textInputSpan{
  position: absolute;
}
              
            
!

JS

              
                /**
 * @author Sakri Rosenstrom
 * http://www.sakri.net
 * https://twitter.com/sakri
 * http://www.devstate.net
 * Sources for this can be found at:
 * https://github.com/sakri/sakriNetCommonJS
 */

(function (window){

    var Sakri = window.Sakri || {};
    window.Sakri = window.Sakri || Sakri;
    
	Sakri.MathUtil = {};
	
	Sakri.MathUtil.PI2 = Math.PI*2;

  Sakri.MathUtil.constrainRadianTo2PI = function(rad){
    return (Sakri.MathUtil.PI2 + rad % Sakri.MathUtil.PI2) % Sakri.MathUtil.PI2;//equally so...
  };
	
}(window));


(function (window){

    var Sakri = window.Sakri || {};
    window.Sakri = window.Sakri || Sakri;

  	Sakri.Geom = {};

    //==================================================
    //=====================::POINT::====================
    //==================================================

    Sakri.Geom.Point = function (x,y){
        this.x = isNaN(x) ? 0 : x;
        this.y = isNaN(y) ? 0 : y;
    };

    Sakri.Geom.Point.prototype.clone = function(){
        return new Sakri.Geom.Point(this.x,this.y);
    };

    Sakri.Geom.Point.prototype.update = function(x, y){
        this.x = isNaN(x) ? this.x : x;
        this.y = isNaN(y) ? this.y : y;
    };

    Sakri.Geom.Point.prototype.equals = function(point){
        return this.x==point.x && this.y==point.y;
    };

    Sakri.Geom.Point.prototype.toString = function(){
        return "{x:"+this.x+" , y:"+this.y+"}";
    };


    
	//==================================================
	//===================::RECTANGLE::==================
	//==================================================

	Sakri.Geom.Rectangle = function (x, y, width, height){
		this.update(x, y, width, height);
	};
	
	Sakri.Geom.Rectangle.prototype.update = function(x, y, width, height){
		this.x = isNaN(x) ? 0 : x;
		this.y = isNaN(y) ? 0 : y;
		this.width = isNaN(width) ? 0 : width;
		this.height = isNaN(height) ? 0 : height;
	};

  
	Sakri.Geom.Rectangle.prototype.getRight = function(){
		return this.x + this.width;
	};
	
	Sakri.Geom.Rectangle.prototype.getBottom = function(){
		return this.y + this.height;
	};

    Sakri.Geom.Rectangle.prototype.getCenterX = function(){
        return this.x + this.width/2;
    };

    Sakri.Geom.Rectangle.prototype.getCenterY = function(){
        return this.y + this.height/2;
    };

    Sakri.Geom.Rectangle.prototype.containsPoint = function(x, y){
        return x >= this.x && y >= this.y && x <= this.getRight() && y <= this.getBottom();
    };

	
	Sakri.Geom.Rectangle.prototype.clone = function(){
		return new Sakri.Geom.Rectangle(this.x, this.y, this.width, this.height);
	};
	
	Sakri.Geom.Rectangle.prototype.toString = function(){
		return "Rectangle{x:"+this.x+" , y:"+this.y+" , width:"+this.width+" , height:"+this.height+"}";
	};
	
}(window));

/**
 * Created by sakri on 27-1-14.
 */
(function (window){

    var Sakri = window.Sakri || {};
    window.Sakri = window.Sakri || Sakri;

    Sakri.BitmapUtil = {};

    Sakri.BitmapUtil.getFirstNonTransparentPixelBottomUp = function(canvas){
        var context = canvas.getContext("2d");
        var y, i, rowData;
        for(y = canvas.height - 1; y>-1; y--){
            rowData = context.getImageData(0, y, canvas.width, 1).data;
            for(i=0; i<rowData.length; i+=4){
                if(rowData[i+0] + rowData[i+1] + rowData[i+2] + rowData[i+3] > 0){
                    return new Sakri.Geom.Point(i/4, y);
                }
            }
        }
        return null;
    };

}(window));

/**
 * Created by sakri on 27-1-14.
 * has a dependecy on Sakri.Geom
 * has a dependecy on Sakri.BitmapUtil
 */

(function (window){

    var Sakri = window.Sakri || {};
    window.Sakri = window.Sakri || Sakri;

    Sakri.CanvasTextUtil = {};


    Sakri.CanvasTextUtil.resizeCanvasToString = function(canvas, string, fontProps){
        var context = canvas.getContext('2d');

        context.font = fontProps.getFontString();
        context.textBaseline = "top";

        var textWidth = context.measureText(string).width;
        canvas.width = textWidth;
        canvas.height = fontProps.fontSize * 1.5;//normally descenders shouldn't go below this

        //after a resize of a canvas, we have to reset these properties
        context.font =  fontProps.getFontString();
        context.textBaseline = "top";
        context.fillStyle = "#FF0000";
        context.fillText(string, 0, 0);

        var textHeight = Sakri.BitmapUtil.getFirstNonTransparentPixelBottomUp(canvas).y;//this returns a point
        canvas.width = textWidth;
        canvas.height = textHeight;
    }

    //this method renders text into a canvas, then resizes the image by shrinkPercent
    //loops through the non transparent pixels of the resized image and returns those as an array
    //fontProperties should be an object of type Sakri.CanvasTextProperties
    Sakri.CanvasTextUtil.createTextParticles = function(text, shrinkPercent, fontProps){
        var canvas = document.createElement('canvas');
        Sakri.CanvasTextUtil.resizeCanvasToString(canvas, text, fontProps);
        var context = canvas.getContext('2d');

        //after a resize of a canvas, we have to reset these properties
        context.font =  fontProps.getFontString();;
        context.textBaseline = "top";
        context.fillStyle = "#FF0000";
        context.fillText(text, 0, 0);

        var shrunkenCanvas = document.createElement('canvas');
        shrunkenCanvas.width = Math.round(canvas.width * shrinkPercent);
        shrunkenCanvas.height = Math.round(canvas.height * shrinkPercent);
        var shrunkenContext = shrunkenCanvas.getContext('2d');
        shrunkenContext.drawImage(canvas, 0, 0, shrunkenCanvas.width , shrunkenCanvas.height  );

        var pixels = shrunkenContext.getImageData(0, 0, shrunkenCanvas.width, shrunkenCanvas.height);
        var data = pixels.data;
        var particles = [];
        var i, x, y;
        for(i = 0; i < data.length; i += 4) {
            if(data[i]>200){
                x = ((i/4)%shrunkenCanvas.width)/shrinkPercent;
                y = Math.floor((i/4)/shrunkenCanvas.width)/shrinkPercent;
                particles.push(new Sakri.Geom.Point(x, y));
            }
        }
        delete canvas;//not sure if necessary?!
        delete shrunkenCanvas;
        return particles;
    };


    Sakri.CanvasTextUtil.createImagesFromString = function(string, fillStyle, strokeStyle, strokeWidth, fontProps){
        var fontString = fontProps.getFontString();
        var characters = string.split("");
        var images = [];
        var context, image, metrics, i, character;
        var canvas = document.createElement("canvas");

        for(i=0; i<characters.length; i++){
            character = characters[i];

            Sakri.CanvasTextUtil.resizeCanvasToString(canvas, character, fontProps);
            context = canvas.getContext("2d");

            //these properties have to be set twice as they vanish after setting a canvas width and height
            context = canvas.getContext("2d");
            context.textBaseline = "top";
            context.font = fontString;

            image = new Image();
            image.width = canvas.width;
            image.height = canvas.height;

            if(fillStyle){
                context.fillStyle = fillStyle;
                context.fillText (character,0, 0);
            }
            if(strokeStyle){
                context.strokeStyle = strokeStyle;
                context.lineWidth = strokeWidth;
                context.strokeText(character, 0, 0);
            }

            image.src = canvas.toDataURL();
            images[i] = image;
        }
        delete canvas;
        return images;
    };


    //=========================================================================================
    //==============::CANVAS TEXT PROPERTIES::====================================
    //========================================================

    Sakri.CanvasTextProperties = function(fontWeight, fontStyle, fontSize, fontFace){
        this.setFontWeight(fontWeight);
        this.setFontStyle(fontStyle);
        this.setFontSize(fontSize);
        this.fontFace = fontFace ? fontFace : "sans-serif";
    };

    Sakri.CanvasTextProperties.NORMAL = "normal";
    Sakri.CanvasTextProperties.BOLD = "bold";
    Sakri.CanvasTextProperties.BOLDER = "bolder";
    Sakri.CanvasTextProperties.LIGHTER = "lighter";

    Sakri.CanvasTextProperties.ITALIC = "italic";
    Sakri.CanvasTextProperties.OBLIQUE = "oblique";


    Sakri.CanvasTextProperties.prototype.setFontWeight = function(fontWeight){
        switch (fontWeight){
            case Sakri.CanvasTextProperties.NORMAL:
            case Sakri.CanvasTextProperties.BOLD:
            case Sakri.CanvasTextProperties.BOLDER:
            case Sakri.CanvasTextProperties.LIGHTER:
                this.fontWeight = fontWeight;
                break;
            default:
                this.fontWeight = Sakri.CanvasTextProperties.NORMAL;
        }
    };

    Sakri.CanvasTextProperties.prototype.setFontStyle = function(fontStyle){
        switch (fontStyle){
            case Sakri.CanvasTextProperties.NORMAL:
            case Sakri.CanvasTextProperties.ITALIC:
            case Sakri.CanvasTextProperties.OBLIQUE:
                this.fontStyle = fontStyle;
                break;
            default:
                this.fontStyle = Sakri.CanvasTextProperties.NORMAL;
        }
    };

    Sakri.CanvasTextProperties.prototype.setFontSize = function(fontSize){
        if(fontSize && fontSize.indexOf && fontSize.indexOf("px")>-1){
            var size = fontSize.split("px")[0];
            fontProperites.fontSize = isNaN(size) ? 24 : size;//24 is just an arbitrary number
            return;
        }
        this.fontSize = isNaN(fontSize) ? 24 : fontSize;//24 is just an arbitrary number
    };

    Sakri.CanvasTextProperties.prototype.getFontString = function(){
        return this.fontWeight + " " + this.fontStyle + " " + this.fontSize + "px " + this.fontFace;
    };

}(window));





//========================
//general properties for demo set up
//========================

var canvas;
var context;
var htmlBounds;
var bounds;
var minimumStageWidth = 250;
var minimumStageHeight = 250;
var resizeTimeoutId = -1;
var canvasContainer;

var readyStateCheckInterval = setInterval( function() {
    if (document.readyState === "complete") {
        clearInterval(readyStateCheckInterval);
        init();
    }
}, 10);

function init(){
    canvas = document.createElement('canvas');
    canvas.style.position = "absolute";
    context = this.canvas.getContext("2d");
    canvasContainer = document.getElementById("canvasContainer");
    canvasContainer.appendChild(this.canvas);
    window.onresize = resizeHandler;
    commitResize();
}

function getWidth( element ){return Math.max(element.scrollWidth,element.offsetWidth,element.clientWidth );}
function getHeight( element ){return Math.max(element.scrollHeight,element.offsetHeight,element.clientHeight );}

//avoid running resize scripts repeatedly if a browser window is being resized by dragging
function resizeHandler(){
    context.clearRect(0,0,bounds.width, bounds.height);
    clearTimeout (resizeTimeoutId);
    clearTimeoutsAndIntervals();
    resizeTimeoutId = setTimeout(commitResize, 300 );
}

function commitResize(){
    htmlBounds = new Sakri.Geom.Rectangle(0,0, getWidth(canvasContainer) , getHeight(canvasContainer));
    if(htmlBounds.width>=800){
        canvas.width = 800;
        canvas.style.left = htmlBounds.getCenterX() - 400+"px";
    }else{
        canvas.width = htmlBounds.width;
        canvas.style.left ="0px";
    }
    if(htmlBounds.height>600){
       canvas.height = 600;
       canvas.style.top = htmlBounds.getCenterY() - 300+"px";
    }else{
        canvas.height = htmlBounds.height;
        canvas.style.top ="0px";
    }
    bounds = new Sakri.Geom.Rectangle(0,0, canvas.width, canvas.height);

    context.clearRect(0,0,bounds.width, bounds.height);

    if(bounds.width<minimumStageWidth || bounds.height<minimumStageHeight){
        stageTooSmallHandler();
        return;
    }

    var textInputSpan = document.getElementById("textInputSpan");
    textInputSpan.style.top = htmlBounds.getCenterY() + 300+"px";
    textInputSpan.style.left = (htmlBounds.getCenterX() - getWidth(textInputSpan)/2)+"px";

    start();
}

function stageTooSmallHandler(){
    var warning = "Sorry, bigger screen required :(";
    var props = new Sakri.CanvasTextProperties(null,null,24);
    context.font = props.getFontString();
    context.fillText(warning, bounds.getCenterX() - context.measureText(warning).width/2, bounds.getCenterY()-12);
}

//========================
//Demo specific properties
//========================

function clearTimeoutsAndIntervals(){
    animating = false;
    clearTimeout(explodeTimeOut);
}

var animating = false;
var explodeTimeOut = -1;
var particles;
var choppedTextImages;
var maxSpeed = 8;
var minSpeed = 5;
var maxRotationSpeed = Math.PI/15;
var textString = "@SAKRI";
var maxCharacters = 7;
var textStringParticles;
var xOffset;
var yOffset;
var fontProperties = new Sakri.CanvasTextProperties(Sakri.CanvasTextProperties.BOLD, null, 160);
var fillStyle = "#0b0b2e";
var strokeStyle = "#FFFFFF";
var strokeWidth = 2;
var explodeDelay = 70;

function start(){
    choppedTextImages = Sakri.CanvasTextUtil.createImagesFromString(textString, fillStyle, strokeStyle, strokeWidth, fontProperties);
    context.clearRect(0, 0, canvas.width, canvas.height);
    context.fillStyle = fillStyle;
    context.strokeStyle = strokeStyle;
    context.font = fontProperties.getFontString();
    context.textBaseline = "top";
    xOffset = bounds.getCenterX() - context.measureText(textString).width/2;
    yOffset = bounds.getCenterY() - 60;
    context.fillText(textString, xOffset, yOffset);
    context.strokeText(textString, xOffset, yOffset);
    particles = Sakri.CanvasTextUtil.createTextParticles(textString, .11, fontProperties );
    for(var i=0; i<particles.length;i++){
        particles[i].x+=xOffset;
        particles[i].y+=yOffset;
    }
    explodeTimeOut = setTimeout(explode, 1000);
}

function setParticleTargets(){
    var i,particle;
    for(i=0; i<particles.length;i++){
        particle = particles[i];
        particle.xSpeed = Math.random()*maxSpeed *  (Math.random()>.5 ? -1 : 1);
        particle.ySpeed = Math.random()*maxSpeed *  (Math.random()>.5 ? -1 : 1);
        if(particle.xSpeed<minSpeed && particle.ySpeed<minSpeed){
            if(Math.random()>.5){
                particle.xSpeed = (minSpeed + Math.random()*maxSpeed) *  (Math.random()>.5 ? -1 : 1);
            }else{
                particle.ySpeed = (minSpeed + Math.random()*maxSpeed) *  (Math.random()>.5 ? -1 : 1);
            }
        }
        particle.rotationSpeed = (Math.random()*maxRotationSpeed) *  (Math.random()>.5 ? -1 : 1);
        particle.rotation = 0;
        particle.characterIndex = Math.floor(Math.random()*choppedTextImages.length);
        particle.scaleSpeed = .002 + Math.random() * .01;
        particle.scale = .1;
    }
}

function explode(){
    setParticleTargets();
    textStringParticles = [];
    var imgX=0;
    var charImg;
    for(i=0; i<textString.length;i++){
        charImg = choppedTextImages[i];
        textStringParticles[i] = particles.splice(Math.floor(Math.random()*particles.length), 1)[0];
        textStringParticles[i].targetX = xOffset + imgX;
        textStringParticles[i].targetY = yOffset;
        textStringParticles[i].characterIndex = i;
        textStringParticles[i].scaleSpeed = .002;
        imgX += charImg.width;
    }
    explodeDelay = 30;
    animating = true;
    loop();
}

function loop(){
    updateParticles();
    if(animating){
        window.requestAnimationFrame(loop, canvas);
    }
}

function updateParticles(){
    explodeDelay--;

    //show trailers only while "exploding"
    if(explodeDelay < 0 && particles.length){
        context.fillStyle = "#FFFFFF";
        context.globalAlpha = .5;
        context.fillRect(0, 0, canvas.width, canvas.height);
        context.globalAlpha = 1;
    }else{
        context.clearRect(0, 0, canvas.width, canvas.height);
    }

    var i,particle, charImg;
    for(i=particles.length-1; i>-1;i--){
        particle = particles[i];
        if(explodeDelay<0){
            particle.x += particle.xSpeed;
            particle.y += particle.ySpeed;
            particle.scale += particle.scale>=1 ? 0 : particle.scaleSpeed;
        }
        particle.rotation += particle.rotationSpeed;
        particle.rotation = Sakri.MathUtil.constrainRadianTo2PI(particle.rotation);
        if(!bounds.containsPoint(particle.x, particle.y)){
            particles.splice(i,1);
        }else{
            charImg = choppedTextImages[particle.characterIndex];
            context.translate(particle.x, particle.y);
            context.rotate(particle.rotation);
            context.drawImage(charImg, 0,0,charImg.width, charImg.height, -(charImg.width*particle.scale)/2, -(charImg.height*particle.scale)/2, charImg.width*particle.scale, charImg.height*particle.scale);
            context.setTransform(1,0,0,1,0, 0);
        }
    }

    if(explodeDelay<0){
        for(i=0; i<textStringParticles.length;i++){
            particle = textStringParticles[i];
            charImg = choppedTextImages[particle.characterIndex];
            particle.x += (particle.targetX-particle.x)*.03;
            particle.y += (particle.targetY-particle.y)*.03;
            particle.scale += .005;
            context.drawImage(charImg, 0, 0, charImg.width, charImg.height, particle.x, particle.y, charImg.width*particle.scale, charImg.height*particle.scale);
        }
    }

    if(!particles.length && particle.scale>.99){
        clearTimeoutsAndIntervals();
        start();
    }
}

function changeText(){
    var textInput = document.getElementById("textInput");
    if(textInput.value && textInput.text!=""){
        if(textInput.value.length > maxCharacters){
            alert("Sorry, there is only room for "+maxCharacters+" characters. Try a shorter name.");
            return;
        }
        if(textInput.value.indexOf(" ")>-1){
            alert("Sorry, no support for spaces right now :(");
            return;
        }
        textString = textInput.value;
        clearTimeoutsAndIntervals();
        setTimeout(start,100);
    }
}
              
            
!
999px

Console