Skip to content

Commit

Permalink
single underline pixel perfect works with ratio
Browse files Browse the repository at this point in the history
  • Loading branch information
wentin committed Jan 23, 2015
1 parent b60fb60 commit f66d1b2
Show file tree
Hide file tree
Showing 7 changed files with 141 additions and 124 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1 +1,3 @@
.DS_Store
**/.sass-cache
**/.sass-cache/*
Binary file not shown.
Binary file not shown.
Binary file not shown.
6 changes: 4 additions & 2 deletions index.html
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@
<script type="text/javascript" src="js/baseline-ratio.js"></script>
<script type="text/javascript" src="js/underline.js"></script>
<script type="text/javascript" src="js/guitar-string.js"></script>
<script type="text/javascript" src="js/class.js"></script>
<script type="text/javascript" src="js/single-underline.js"></script>
<script type="text/javascript" src="js/multiple-underline.js"></script>
</head>
<body>
<header>
Expand All @@ -32,6 +33,7 @@ <h1>A quest to pixel-perfect underline <br>
with canvas, and music</h1>
</header>
<article>
<a href="#" class="underline" style="line-height: 1.4em; font-size: 24px">parapsychologist</a>
<h2>Manifesto</h2>

<p>A javascript library that sets out to do one simple job: draw and animate the most perfect and playful text underline</p>
Expand All @@ -58,7 +60,7 @@ <h2>Idea</h2>
<li>clear the descenders,</li>
<li>(perhaps) have a separate style for visited links.</li>
</ul>
<p>In addition to these above, it should also
<p>In addition to these above, it should also
</p>
<ul>
<li>have no ghost pixels</li>
Expand Down
122 changes: 0 additions & 122 deletions js/class.js → js/multiple-underline.js
Original file line number Diff line number Diff line change
@@ -1,124 +1,3 @@
var multiplyValue = function(value, multiplier){
var str = value;
var m = multiplier;
var result = str.match(/(\d*\.?\d*)(.*)/);
//http://stackoverflow.com/questions/2868947/split1px-into-1px-1-px-in-javascript
return result[1] * m + result[2];
}
function SingleUnderline(element, underlineStyles, elementStyles) {
//ctor
this.element = element;

this.text = this.element.textContent;

this.underlineStyles = underlineStyles;

this.elementStyles = elementStyles;
this.redrawActive = false;

this.canvas = document.createElement("canvas");
this.ctx = this.canvas.getContext('2d');

this.ratio = window.devicePixelRatio;
this.canvas.width = this.elementStyles.width*this.ratio;
this.canvas.height = this.elementStyles.height*this.ratio;
this.element.appendChild(this.canvas);
this.canvas.style.width = this.elementStyles.width + 'px';

this.ctx.font = this.font = this.elementStyles.fontStyle + ' '
+ multiplyValue(this.elementStyles.fontSize, this.ratio) + ' '
+ this.elementStyles.fontFamily;

// console.log(this.ratio);
// determine the text-underline-width / strokeWidth
this.dotWidth = this.ctx.measureText('.')['width'];

if (this.underlineStyles['text-underline-width'] == "auto") {
// if set to auto, calculate the optimized width based on font
if (this.dotWidth/6 <= 2) {
this.strokeWidth = Math.round( this.dotWidth/3 )/2;
} else {
this.strokeWidth = Math.round( this.dotWidth/6 );
}
// console.log(this.strokeWidth);
} else {
//if set to px value
this.strokeWidth = this.underlineStyles['text-underline-width'];
//get number value
this.strokeWidth = parseFloat(this.strokeWidth)*this.ratio;
}
console.log(this.strokeWidth);

// determine the text-underline-position / underlinePosition
// text-underline-position in ratio
this.underlinePosition = parseFloat(this.elementStyles.height) * this.ratio *
(1 - this.elementStyles.baselinePositionRatio
+ this.underlineStyles['text-underline-position']);

if(this.strokeWidth <= 1 || (this.strokeWidth%2 && this.strokeWidth > 2)) {
this.underlinePosition = Math.round(this.underlinePosition - 0.5) + 0.5;
} else {
this.underlinePosition = Math.round(this.underlinePosition);
}

this.textWidth = this.ctx.measureText(this.text).width;

this.myString = new GuitarString(this.ctx,
new Point(0, this.underlinePosition),
new Point(this.textWidth, this.underlinePosition),
this.strokeWidth, this.underlineStyles['text-underline-color'], this.ratio);
this.drawHoles();

}

SingleUnderline.prototype.clear = function(){
this.redrawActive = this.myString.redrawActive;
// clear
if(this.myString.redrawActive) {
// this.myString.clear();
this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
}
};


SingleUnderline.prototype.update = function(){
// update
if(this.myString.redrawActive) {
this.myString.update();
// this.drawHoles();
}
};


SingleUnderline.prototype.draw = function(){
// draw
if(this.redrawActive) {
this.drawUnderline();
this.drawHoles();
}
};

SingleUnderline.prototype.drawUnderline = function(){
// draw the underline
this.myString.draw();
}

SingleUnderline.prototype.drawHoles = function(){

// draw the font stroke
this.ctx.font = this.font;
this.ctx.textBaseline = 'top';

this.ctx.globalCompositeOperation = "destination-out";

this.ctx.fillStyle = 'green';
this.ctx.beginPath();
this.ctx.fillText(this.text, 0, 0);
this.ctx.lineWidth = 3*this.ratio + this.strokeWidth;
this.ctx.strokeStyle = 'blue';
this.ctx.beginPath();
this.ctx.strokeText(this.text, 0, 0);
}

function MultipleUnderline(element, underlineStyles, elementStyles) {
//ctor
Expand Down Expand Up @@ -356,4 +235,3 @@ MultipleUnderline.prototype.drawHoles = function(){

}
}

135 changes: 135 additions & 0 deletions js/single-underline.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
var multiplyValue = function(value, multiplier){
var str = value;
var m = multiplier;
var result = str.match(/(\d*\.?\d*)(.*)/);
//http://stackoverflow.com/questions/2868947/split1px-into-1px-1-px-in-javascript
return result[1] * m + result[2];
}

var optimalStrokeWidthPos = function(strokeWidth, posY){
if ( strokeWidth < 1) {
posY = Math.round(posY - 0.5) + 0.5;
} else if ( strokeWidth >= 1 ) {
strokeWidth = Math.round( strokeWidth );
if ( strokeWidth % 2 ){
// odd, posY -> 0.5
posY = Math.round(posY - 0.5) + 0.5;
} else {
// even, posY -> 1
posY = Math.round(posY);
}
}
return {
strokeWidth: strokeWidth,
posY: posY
}
}

function SingleUnderline(element, underlineStyles, elementStyles) {
//ctor
this.element = element;

this.text = this.element.textContent;

this.underlineStyles = underlineStyles;

this.elementStyles = elementStyles;
this.redrawActive = false;

this.canvas = document.createElement("canvas");
this.ctx = this.canvas.getContext('2d');

this.ratio = window.devicePixelRatio;
this.canvas.width = this.elementStyles.width*this.ratio;
this.canvas.height = this.elementStyles.height*this.ratio;
this.element.appendChild(this.canvas);
this.canvas.style.width = this.elementStyles.width + 'px';

this.ctx.font = this.font = this.elementStyles.fontStyle + ' '
+ multiplyValue(this.elementStyles.fontSize, this.ratio) + ' '
+ this.elementStyles.fontFamily;

// determine the text-underline-width / strokeWidth
dotWidth = this.ctx.measureText('.')['width'];
if (this.underlineStyles['text-underline-width'] == "auto") {
// if set to auto, calculate the optimized width based on font
this.strokeWidth = dotWidth/6;
} else {
//if set to px value, todo: other unit such as em?
this.strokeWidth = this.underlineStyles['text-underline-width'];
//get number value
this.strokeWidth = parseFloat(this.strokeWidth)*this.ratio;
}


// determine the text-underline-position / underlinePosition
// text-underline-position in ratio, todo: default and user set position ratio
this.underlinePosition = parseFloat(this.elementStyles.height) * this.ratio *
(1 - this.elementStyles.baselinePositionRatio
+ this.underlineStyles['text-underline-position']);

var adjustValue = optimalStrokeWidthPos(this.strokeWidth, this.underlinePosition);
this.strokeWidth = adjustValue.strokeWidth;
this.underlinePosition = adjustValue.posY;

this.textWidth = this.ctx.measureText(this.text).width;

this.myString = new GuitarString(this.ctx,
new Point(0, this.underlinePosition),
new Point(this.textWidth, this.underlinePosition),
this.strokeWidth, this.underlineStyles['text-underline-color'], this.ratio);
this.drawHoles();

}

SingleUnderline.prototype.clear = function(){
this.redrawActive = this.myString.redrawActive;
// clear
if(this.myString.redrawActive) {
// this.myString.clear();
this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
}
};


SingleUnderline.prototype.update = function(){
// update
if(this.myString.redrawActive) {
this.myString.update();
// this.drawHoles();
}
};


SingleUnderline.prototype.draw = function(){
// draw
if(this.redrawActive) {
this.drawUnderline();
this.drawHoles();
}
};

SingleUnderline.prototype.drawUnderline = function(){
// draw the underline
this.myString.draw();
}

SingleUnderline.prototype.drawHoles = function(){

// draw the font stroke
this.ctx.font = this.font;
this.ctx.textBaseline = 'top';

this.ctx.globalCompositeOperation = "destination-out";

this.ctx.lineWidth = 2*this.ratio + this.strokeWidth*2;
console.log(this.ctx.lineWidth);
this.ctx.strokeStyle = 'blue';
this.ctx.beginPath();
this.ctx.strokeText(this.text, 0, 0);

this.ctx.fillStyle = 'green';
this.ctx.beginPath();
this.ctx.fillText(this.text, -1, 0);
}

0 comments on commit f66d1b2

Please sign in to comment.