Create a circular or triangular shaped board - css

I'm trying to create a game board ( similar to a chess board) for a game in react, and I would like to arrenge the cells (rectangular divs, that take they're value from a state array) in different shapes ( circle, triangle, oval etc) thus creating a board of that shape.
I only know how to arrange divs with CSS flex or grid and that is always rectangular formation (as far as I know)
How can I achieve this?
Any help will be appreciated, thanks!

To dynamically position elements, you need to arrange them in code. We can do that by creating the elements in code, then setting their position programmatically. I'm working off this jsfiddle
First, the HTML:
<div id="my_container" />
It couldn't get simpler. We just want somewhere we can put our elements. Now, for minimal styling, we've got:
#my_container {
position: relative;
width: 200px;
height: 200px;
background-color: powderblue
}
.cell {
background-color: red;
width: 5px;
height: 5px;
position: absolute;
}
I've added colors so that you can see the elements in the generated output, but they're not necessary. What is necessary is those position lines. Making the #container div relative lets it behave nicely with whatever it's placed in, and making each of the .cell divs absolute causes them to be placed on absolute coordinates relative to their parent, which means we can put them where we want. Ok, what's next?
The cells. I assume your cells already exist, but since I don't have them, I've created code to build these cells, shown below:
const container = document.getElementById("my_container");
const number_of_cells = 30;
const cells = [];
for (var i = 0; i < number_of_cells; i++)
{
const newDiv = document.createElement("div");
newDiv.classList.add("cell");
container.appendChild(newDiv)
cells.push(newDiv);
}
The goal here is to put the cells as children of #my_container and also create an array of all the cells. (I think maybe this step is redundant but I don't care...It makes the next step easier.)
function PositionCells(cells, x, y, radius)
{
const incr_angle = (2*Math.PI)/cells.length;
for (let i = 0; i < cells.length; i++) {
var new_x = x+radius * Math.cos(incr_angle * i);
var new_y = y+radius * Math.sin(incr_angle * i);
cells[i].style.left = new_x+'px';
cells[i].style.top = new_y+'px';
}
}
PositionCells(cells, 50,50,30);
Basically what I've done is written a function to position all the cells in an ordered, programmatic way. If you want other shapes, you can pretty easily do the math to come up with where to position them.
And that's it!

Related

Show me a Javascript implementation of webkitConvertPointFromPageToNode

The webkitConvertPointFromPageToNode(in Node node, in WebKitPoint p) method is awesome; give it a DOM node and a point in page-coordinates (say, the mouse cursor position) and it will give a coordinate back to you in that node's local coordinate system. Unfortunately, it's currently only available in webkit.
# Choose a node into which we'll map the mouse coordinates
node = $('#subjectElement').get(0)
handleMouseMove = (e) ->
# Convert the mouse position to a Point
mousePoint = new WebKitPoint(e.pageX, e.pageY)
# Convert the mouse point into node coordinates using WebKit
nodeCoords = webkitConvertPointFromPageToNode(node, mousePoint)
# Attach a handler to track the mouse position
$(document).on 'mousemove', handleMouseMove
I've thrown my entire math-brain at the problem, but no matter how close I get, my implementation falls apart with one extra level of composition, or the application of 3D perspective.
It's time for a convertPointFromPageToNode polyfill that works as well as the WebKit implementation, in 3D. #4esn0k gave one a shot, but it only solves the 2D case.
Can you write one that makes this JSFiddle work?
http://jsfiddle.net/steveluscher/rA27K/
This seems like an amazing question, but there is an ALMOST duplicate right here: How to get the MouseEvent coordinates for an element that has CSS3 Transform? but nobody is looking at my answer there and this seems to be much more general so I'll post it again here, with a few modifications to make it more clear:
Basically, it works by doing this: split the element you are trying to find relative coordinates for, and split it into 9 smaller elements. Use document.elementFromPoint to find if the coordinate is over that mini-element. If it is, split that element into 9 more elements, and keep doing this until a pretty accurate coordinate is possible. Then use getBoundingClientRect to find the on-screen coordinates of that mini-element. BOOM!
jsfiddle: http://jsfiddle.net/markasoftware/rA27K/8/
Here is the JavaScript function:
function convertPointFromPageToNode(elt,coords){
///the original innerHTML of the element
var origHTML=elt.innerHTML;
//now clear it
elt.innerHTML='';
//now save and clear bad styles
var origPadding=elt.style.padding=='0px'?'':elt.style.padding;
var origMargin=elt.style.margin=='0px'?'':elt.style.margin;
elt.style.padding=0;
elt.style.margin=0;
//make sure the event is in the element given
if(document.elementFromPoint(coords.x,coords.y)!==elt){
//reset the element
elt.innerHTML=origHTML;
//and styles
elt.style.padding=origPadding;
elt.style.margin=origMargin;
//we've got nothing to show, so return null
return null;
}
//array of all places for rects
var rectPlaces=['topleft','topcenter','topright','centerleft','centercenter','centerright','bottomleft','bottomcenter','bottomright'];
//function that adds 9 rects to element
function addChildren(elt){
//loop through all places for rects
rectPlaces.forEach(function(curRect){
//create the element for this rect
var curElt=document.createElement('div');
//add class and id
curElt.setAttribute('class','offsetrect');
curElt.setAttribute('id',curRect+'offset');
//add it to element
elt.appendChild(curElt);
});
//get the element form point and its styling
var eltFromPoint=document.elementFromPoint(coords.x,coords.y);
var eltFromPointStyle=getComputedStyle(eltFromPoint);
//Either return the element smaller than 1 pixel that the event was in, or recurse until we do find it, and return the result of the recursement
return Math.max(parseFloat(eltFromPointStyle.getPropertyValue('height')),parseFloat(eltFromPointStyle.getPropertyValue('width')))<=1?eltFromPoint:addChildren(eltFromPoint);
}
//this is the innermost element
var correctElt=addChildren(elt);
//find the element's top and left value by going through all of its parents and adding up the values, as top and left are relative to the parent but we want relative to teh wall
for(var curElt=correctElt,correctTop=0,correctLeft=0;curElt!==elt;curElt=curElt.parentNode){
//get the style for the current element
var curEltStyle=getComputedStyle(curElt);
//add the top and left for the current element to the total
correctTop+=parseFloat(curEltStyle.getPropertyValue('top'));
correctLeft+=parseFloat(curEltStyle.getPropertyValue('left'));
}
//reset the element
elt.innerHTML=origHTML;
//restore element styles
elt.style.padding=origPadding;
elt.style.margin=origMargin;
//the returned object
var returnObj={
x: correctLeft,
y: correctTop
}
return returnObj;
}
IMPORTANT!!! You must also include this CSS for it to work:
.offsetrect{
position: absolute;
opacity: 0;
height: 33.333%;
width: 33.333%;
}
#topleftoffset{
top: 0;
left: 0;
}
#topcenteroffset{
top: 0;
left: 33.333%;
}
#toprightoffset{
top: 0;
left: 66.666%;
}
#centerleftoffset{
top: 33.333%;
left: 0;
}
#centercenteroffset{
top: 33.333%;
left: 33.333%;
}
#centerrightoffset{
top: 33.333%;
left: 66.666%;
}
#bottomleftoffset{
top: 66.666%;
left: 0;
}
#bottomcenteroffset{
top: 66.666%;
left: 33.333%;
}
#bottomrightoffset{
top: 66.666%;
left: 66.666%;
}
ALSO: I modified a little of your css by giving the "grandfather" div an id and referencing to it in your css using #div1 instead of div because my code generates divs, and your div styles were also applying to the ones my code uses and messed it up
ONE LAST THING: I don't know CoffeeScript so I adjusted your code to make it pure JavaScript. Sorry about that.
I have written some TypeScript code which does some transformations:
jsidea core library. Its not stable yet (pre alpha).
You can use it like that:
Create you transform instance:
var transformer = jsidea.geom.Transform.create(yourElement);
The box model you want to transform to (default:"border"):
var toBoxModel = "border";
The box model where your input coordinates coming from (default:"border"):
var fromBoxModel = "border";
Tranform your global coordinates (here {x:50, y:100, z: 0}) to local space. The resulting point has 4 components: x, y, z and w.
var local = transformer.globalToLocal(50, 100, 0, toBoxModel, fromBoxModel);
I have implemented some other functions like localToGlobal and localToLocal.
If you want to give a try, just download the release build and use the jsidea.min.js.
I have this issue and started trying to compute the matrix.
I started a library around it: https://github.com/ombr/referentiel
$('.referentiel').each ->
ref = new Referentiel(this)
$(this).on 'click', (e)->
input = [e.pageX, e.pageY]
p = ref.global_to_local(input)
$pointer = $('.pointer', this)
$pointer.css('left', p[0]-1)
$pointer.css('top', p[1]-1)
What do you think ?

Keeping css attributes in an iterated dart template

I'm working on a Dart project where the user is able to add new custom elements at the click of a button. Each custom element is a div containing a table. The divs are resizeable by the user. My problem is that after a new element is added to the list of elements held by my dart file, the sizes of all the divs are automatically reset. Is there any way to add new elements to the template while keeping the attributes of the old ones the same?
Here is my CSS code that deals with the main divs in my custom element:
#superDivContainer {
border: 2px solid black;
resize: both;
display: inline-block;
}
#tableContainer {
height: 125px;
overflow: scroll;
}
Thanks in advance for the help!
When you change the backing list, the template reiterates, destroying all the old elements and recreating them from scratch, so all user resize information is lost.
When you add a new item to the list, you can store all the sizing information of the old elements and then after the item is added set the new elements size:
// Stores size information
List<List> sizes = [];
void add() {
// Store the old sizes
sizes.clear();
ElementList divs = queryAll(".superDivContainer");
for(int i = 0; i < divs.toList().length; i++) {
Element div = divs[i];
sizes.add([div.style.width, div.style.height]);
}
// Add the new item
yourList.add("new");
// Set the sizes of the new elements
Timer timer = new Timer(new Duration(milliseconds: 1), () {
divs = queryAll(".superDivContainer");
for(int i = 0; i < divs.toList().length && i < sizes.length; i++) {
Element div = divs[i];
div.style.width = sizes[i][0];
div.style.height = sizes[i][1];
}
});
}
Two notes:
I changed superDivContainer to be a class instead of an id since it seems that you are applying it to multiple elements; you will need to change your CSS reflect that
The 1 millisecond timer gets around the fact that there is a tiny delay until the new elements are added and accessible

How to print on a specific paper

I'm having problems with printing some text on a A4 paper that has 24 labels.
Basically, in every row there are 3 labels in which comes the name, surname and adress of a person and that label will be used for mails ( it's a sticky label that is sticked on a mail).
So this is the paper. Its characteristics:
There are 10 rows.
The first and last row are the smallest and have height:0.5mm;.
In first and last row there are no cells.
All the rest rows have height:36mm;.
All the cells have width:70mm; and height:36mm;.
In every cell comes a text that is text-align:center; and vertical-align:middle;.
I'm using normalize.css for css reset.
CSS
html,body,table{
width: 100%;
height: 100%;
}
.first, .last{
width: 100%;
height: 5mm;
}
.row{
width: 100%;
height: 36mm;
}
.cell{
width: 70mm;
height: 36mm;
text-align: center;
vertical-align: middle;
}
I'm using Chrome and I turned off the margins on printing.
But still, the last two rows are printed on the next page.
I need all 10 rows on the same page and that their position is fixed ( doesn't shift ) in case if there are multiple pages.
How to fix/achieve that ? Or is there a simpler solution ?
Here is an example of the code.
I've used FPDF class to create a pdf for my labels.
require_once ABSPATH . '/path/to/fpdf.php';
class PDF_MC_Table extends FPDF{
var $widths;
var $aligns;
function SetWidths($w){
//Set the array of column widths
$this->widths=$w;
}
function SetAligns($a){
//Set the array of column alignments
$this->aligns=$a;
}
function Row($data){
//Calculate the height of the row
$nb=0;
for($i=0;$i<count($data);$i++)
$nb=max($nb,$this->NbLines($this->widths[$i],$data[$i]));
$h = 36;// again trial and error until you fnd the desired height of your label
//Issue a page break first if needed
$this->CheckPageBreak($h);
//Draw the cells of the row
for($i=0;$i<count($data);$i++){
$w=$this->widths[$i];
$a=isset($this->aligns[$i]) ? $this->aligns[$i] : 'L';
//Save the current position
$x=$this->GetX();
$y=$this->GetY();
//Draw the border. reset the parameters of the function below as you desire.
$this->Rect($x,$y,$w,$h);
//Print the text. reset the parameters of the function below as you desire. changing the values will resize the boxs.
$this->MultiCell($w,3,$data[$i],0,$a);
//Put the position to the right of the cell. reset the parameters of the function below as you desire. changing the $x and $y will shift the cells.
$this->SetXY($x+$w,$y);
}
//Go to the next line
$this->Ln($h+3);
}
function CheckPageBreak($h){
//If the height h would cause an overflow, add a new page immediately
if($this->GetY()+$h>$this->PageBreakTrigger)
$this->AddPage($this->CurOrientation);
}
function NbLines($w,$txt){
//Computes the number of lines a MultiCell of width w will take
$cw=&$this->CurrentFont['cw'];
if($w==0)
$w=$this->w-$this->rMargin-$this->x;
$wmax=($w-2*$this->cMargin)*1000/$this->FontSize;
$s=str_replace("\r",'',$txt);
$nb=strlen($s);
if($nb>0 and $s[$nb-1]=="\n")
$nb--;
$sep=-1;
$i=0;
$j=0;
$l=0;
$nl=1;
while($i<$nb){
$c=$s[$i];
if($c=="\n"){
$i++;
$sep=-1;
$j=$i;
$l=0;
$nl++;
continue;
}
if($c==' ')
$sep=$i;
$l+=$cw[$c];
if($l>$wmax){
if($sep==-1){
if($i==$j)
$i++;
}
else
$i=$sep+1;
$sep=-1;
$j=$i;
$l=0;
$nl++;
}
else
$i++;
}
return $nl;
}
}
$pdf=new PDF_MC_Table();
$pdf->SetMargins(4, 2);
$pdf->AddPage();
$pdf->SetFont('Arial','',8);
// displays the empty row in the top
$pdf->SetRightMargin(2);
$pdf->SetLeftMargin(4);
$pdf->Cell(0,10,'',1);
$pdf->Ln(10);
$pdf->SetWidths(array(50,50,50));// these are the widths of your cells. this is a trial and error process. increase the values until you find the suitable ones.
$count = 0;
$lables = array(); // your labels array
$l = array();
$j = 0;
// i used this foreach for breaking my plain array into a 2dimentional array- an array of arrays consisting 3 labels in each.
foreach($lables as $i=>$lbl ){
$l[$j][] = $lbl;
if($i%3==2){$j++;} // $i=0,1,2 > $j=0; $i=3,4,5 > $j=2 etc this will break the main labels array as 2D array.
}
// displays the empty row in the bottom.
$pdf->Ln(1);
$pdf->Cell(0,10,'',1);
$pdf->Output();
for further information about the class and methods please refer to http://www.fpdf.org/ At best you need to understand three methods- Multicell(), Cell() and Rect() methods. There are pretty nice explanation of these methods with examples in the site.
Here i've posted my solution and i've changed some code based on your problem. Most of the things are self explanatory. If you need further assist please feel free to comment. Thanks.
reduce the cell and row height sizes if you are no using the sizes in specific, what happens even after turning off margins, printing data goes out of paper length and printer moves it to another page automatically.
thats the best solution i think you can try.
p.s: these kinds of sticky labels usually come with their design software cd which you usually use to set the margins with preview. if you dont have it, only thing you can do is maually adjust the heights as i told above, print on a normal A4 plain paper and tele with the sticky label paper. I used to do it too... worked for me though
Try borderless printing if your printer supports this feature.
Try adding in the styling below and playing with the #page attributes. More info in #page can be found here: https://developer.mozilla.org/en-US/docs/CSS/#page
I've had good luck using the styles defined below to print perfectly on 8.5x11 documents where I can set my own margin and have consistent results out of the browser.
<style type="text/css" media="print">
#page {
size: auto; /* auto is the initial value */
margin: 0mm; /* this affects the margin in the printer settings */
}
body {
background-color: #fff;
border: solid 1px #000 ;
margin: 0px; /* this affects the margin on the content before sending to printer */
padding: 0.25in 0.5in;
}
</style>

Have container fit the width of one of its children, and another children using text-overflow

I'm looking for a way to have a HTML container fit the width of one of its children.
OK I know, this is how it already works by design.
But! I also need another children to collapse with a "text-overflow: ellipsis". Problem is: to apply such a property, you need this children to be in "display: block" mode, which makes it enlarge the container width.
Is there any secret time to achieve what I'm looking for.
Here is a JsFiddle in case you don't get it or want to give it a try.
Edit : by the way, and this is important, I'm targetting specifically Internet Explorer 10.
As watson said, there is no "shrink-to-fit" css rule. So, you have two choices:
Set the size of the .overflow elements manually and statically. So, instead of width:100%, you put width:330px.
Use javascript to resize the .overflow elements dynamically. (I'm assuming you have more than one.) You said you wanted to shrink to the biggest internal div. Let's say you have several divs you might want to shrink to, but you want to shrink to the largest of them. First, you set them all to a class like this:
.good-width{
border: solid 2px salmon;
width:auto; /* necessary for some browsers' offsetWidth */
display:inline-block; /* gives it the width of the contents */
}
And you put javascript something like this at the top of the page:
var goods = document.getElementsByClassName('good-width');
//collect the widest one's width
var maxwidth = 0;
for(var x = 0; x < goods.length; x++) {
if(goods[x].offsetWidth > maxwidth) {
maxwidth = goods[x].offsetWidth;
}
}
//set the width of the overflow divs to match
var overflows = document.getElementsByClassName('overflow');
for(var y = 0; y < overflows.length; y++) {
overflows[y].style.width = maxwidth + 'px';
}
If I misunderstood, and you're trying to match specific overflows to specific good-widths, you should assign each element an id and do things that way:
document.getElementById('overflowID').style.width = document.getElementById('good-widthID').offsetWidth + 'px';
If it were my website, I would actually combine both #1 and #2, in order to have it look at least decent for those who don't have javascript. That is, you set a static width to the overflow things that isn't too far off, then allow the javascript to overwrite it if it can.

CSS positioning with JavaScript-generated divs not working how I expected

First off, I must apologize. CSS positioning has always been the bane of my existence and this is likely something simple that I'm just completely missing...
Anyway, I have a JS script that's generating divs. Each div is within the parent #container which is absolute positioned. CSS below:
#container{
position: absolute;
}
#container div{
position: relative;
}
The function creating the divs is:
function newLine(){
var id_num = ++line;
var _new;
var i;
for(i = 0; i < width; i++){
_new = document.createElement('div');
_new.innerHTML = randomChar();
_new.id = id_num;
_new.style.left = i*10+'px';
_new.style.top = 0;
document.getElementById('container').appendChild(_new);
}
}
Everything above is properly initialized. The left positioning works perfectly. The only issue is the vertical positioning. Instead of all the row displaying next to each other, they're progressively increasing away from the top of the div. I'm sure this is something trivial that I'm completely looking over, but I'm stumped... Help would be very much appreciated!
The rows as position: relative - this lays them out statically and then moves them the specified number of pixels. You want to use absolute positioning.

Resources