I have a background image with object of some shape (example below). I would like to add new images - layers (simple div with position:absolute - left/top) to this image and only on the shape where I want.
And then with PHP code added images (for example 10, 50,..) to only this shape and no other place:
Is this possible to do on any simple way with PHP/JS/jquery/...? I just need to pass how many items and that many images is added to that area...
Here's a complete rewrite of my answer.
Given a random picture, fit many smaller pictures only where some color is matched:
I decided to go with a crab as a starting pictures, because crabs are cool:
I only want to add red dots where there is blue in the picture
To do this, I will split my answer in 3 sections:
HTML
I start with a very simple HTML file:
<html>
<head>
<title>Crab Example</title>
</head>
<body>
<div>
<h1>Dots on the crab example</h1>
</div>
<div id="crabSection">
<img src="crab.png">
</div>
</body>
</html>
This gives us a starting point for rendering our dotted crab!
PHP
Now, in PHP, I open both my crab.png and my dot.png to analyze their content, and locate random positions where the dot.png can fit in an all blue section. Right after the <img src="crab.png">, I inserted the following:
<?php
$crab = imagecreatefrompng("crab.png");
$dot = imagecreatefrompng("dot.png");
$numDesiredDots = 10;
$numCreatedDots = 0;
$crabWidth = imagesx($crab);
$crabHeight = imagesy($crab);
$dotWidth = imagesx($dot);
$dotHeight = imagesy($dot);
$spawnableWidth = $crabWidth - $dotWidth;
$spawnableHeight = $crabHeight - $dotHeight;
srand(time());
$testingForDotSubpart = imagecreatetruecolor($dotWidth, $dotHeight);
$validCoordinates = array();
$invalidCoordinates = array();
$colorWereLookingFor = 0xFF; // ARGB - our crab is blue
Here, a few details:
$numDesiredDots is hardcoded to 10, but it could easily be a parameter!
$spawnableWidth and $spawnableHeight represent the greatest coordinate a dot can be placed in without going out of the picture
srand(time()); is simply used to have a different random everytime
$testingForDotSubpart is a small image I will be using to test a given coordinate to see if it only contains pixels of the right color
$colorWereLookingFor is set to blue now, because my crab is blue, if you wanted red, it should be something like 0xFF0000. For this example I used the same PNG for the HTML and the image processing, but you could easily just create a mask for the image processing and have a full color image for the HTML.
Now, we need to create valid coordinates for each dot, which is done with the following php:
while($numCreatedDots < $numDesiredDots)
{
$randomX = rand() % $spawnableWidth;
$randomY = rand() % $spawnableHeight;
imagecopy($testingForDotSubpart, $crab, 0, 0, $randomX, $randomY, $dotWidth, $dotHeight);
$valid = true;
for($x = 0; $x < $dotWidth; $x++)
{
for($y = 0; $y < $dotHeight; $y++)
{
if(imagecolorat($testingForDotSubpart, $x, $y) != $colorWereLookingFor)
{
$valid = false;
break 2;
}
}
}
if($valid)
{
array_push($validCoordinates, array('x' => $randomX, 'y' => $randomY));
$numCreatedDots++;
}
else
{
// you can get rid of this else, it's just to show you how many fails there are
array_push($invalidCoordinates, array('x' => $randomX, 'y' => $randomY));
}
}
Again, some explanation:
We iterate as long as we haven't created all the dots we want, for a very complex image, this might take too much time, you could add a maximum number of tries
We start by creating a random X,Y coordinate
We copy the small window where the dot could end up
We test all the pixels inside this window to make sure they are of the right color
If the window is valid, we add the coordinates to an array
For debugging purposes, I added an $invalidCoordinates array to show how many tries failed - the more complex the picture, the more fails there will be
Now that we have computed all our positions, we need to clean up the resources:
imagedestroy($testingForDotSubpart);
imagedestroy($dot);
imagedestroy($crab);
Finally, I added some debug output that you can get rid of, but we need to output the dots on the crab! To show you that each dot is unique, I attached a JavaScript alert that shows the dot index:
echo "<p>Valid Coords: <br>";
foreach($validCoordinates as $coord)
{
echo "X: " . $coord['x'] . " Y: " . $coord['y'] . "<br>\n";
}
echo "<br>Invalid Coords " . count($invalidCoordinates) . "</p>\n";
// Now add the dots on the crab!
for($i = 0; $i < count($validCoordinates); $i++)
{
$coord = $validCoordinates[$i];
echo "<div class='dot' style='left:".$coord['x'].";top:".$coord['y'].";'><a href='javascript:alert(".$i.");'><img src='dot.png'></a></div>\n";
}
?>
Here, I am using the style left and top to give precise pixel positioning to the dots. To have them match precisely with the parent picture, we need to use position:relative; and position:absolute; as described in the next section.
CSS
As you can see, I'm using a class for the div, this is to take advantage of the relative positioning. I added at the top of the file, right after the title, the following
#crabSection { position:relative; }
.dot { margin: 0px 0px 0px 0px; position:absolute; }
Result
Here's a run of the given script... you could easily save the HTML generated so you don't have to recompute positions everytime:
Hope this helps!
Edit: here's the full code, if you need it: pastebin
Edit 2: note that there is no overlap checking, you might want to check that the rectangle defined by $randomX, $randomY, $randomX + $dotWidth and $randomY + $dotHeight doesn't overlap an existing rectangle from the coordinates in $validCoordinates.
Edit 3: Once generated you can simply open the source of the page, and copy the div to your HTML so it's not regenerated everytime.
<div class='dot' style='left:100;top:105;'><a href='javascript:alert(0);'><img src='dot.png'></a></div>
<div class='dot' style='left:150;top:151;'><a href='javascript:alert(1);'><img src='dot.png'></a></div>
<div class='dot' style='left:128;top:73;'><a href='javascript:alert(2);'><img src='dot.png'></a></div>
<div class='dot' style='left:144;top:93;'><a href='javascript:alert(3);'><img src='dot.png'></a></div>
<div class='dot' style='left:164;top:91;'><a href='javascript:alert(4);'><img src='dot.png'></a></div>
<div class='dot' style='left:108;top:107;'><a href='javascript:alert(5);'><img src='dot.png'></a></div>
<div class='dot' style='left:22;top:101;'><a href='javascript:alert(6);'><img src='dot.png'></a></div>
<div class='dot' style='left:54;top:151;'><a href='javascript:alert(7);'><img src='dot.png'></a></div>
<div class='dot' style='left:32;top:121;'><a href='javascript:alert(8);'><img src='dot.png'></a></div>
<div class='dot' style='left:142;top:87;'><a href='javascript:alert(9);'><img src='dot.png'></a></div>
Also, as long as the color mask described by the $crab picture opened in PHP, you can change the img src to something else if you wanted a colorful crab. I added a crabc.png file which is now used by my img, but it still has the same outline as the crab.png file:
Which gives this final look:
Hmm, I'm not sure it's possible to add them to fit a shape unless that shape is created in svg.
You could have a div with an image in it which is an inverted arrow i.e. the image is white with the arrow cut out and a bgcolor on the div.
Then you could add the dots to the same div and just set their z-index to lower than the arrow image.
Then the dots will only appear in the arrow, only problem is some might be added but not be visible (only a problem if you need the user to interact with them).
What exactly do you want to do with the dots?
Related
I'm looking to write some PHP logic to arrange a number of images, of different sizes, within divs on a page. The scenarios is this:
I have an array of images (from a MySQL lookup) - anywhere up to 100 images.
The images will be one of a set catalogue of sizes (i.e. 60x40, 120x40, 120x60, or 120x180)
I am arranging the images into divs of a set size (120x180).
The logic should work as follows:
Is there enough vertical height in this div for this image to fit in?
If so, is there enough horizontal width in this line for this image to fit?
If there is enough width in this line, then place the image in this line.
If there isn't enough width in this line, then place this image in the next line down.
If there isn't enough vertical height in this div for fit this image, then create a new div, and place the image at the top of that new div.
Repeat this for each image until each image has been placed.
The end result should be number of 120x180 divs, each one filled with images assorted from 60x40, 120x40, 120x60, or 120x180 in size, with no blank spaces.
How can I code this in PHP?
I started with this, but it (obviously) doesn't work:
$pageCount = 1;
$pageWith = 120;
$pageHeight = 180;
echo "<div id='pageFrame' style='width:120px; height:180px;'>";
foreach ($row_ as $Image) {
// Check Virtical Space on the page
if($Image['height'] < $pageHeight)
{
// Check width on page
if($Image['width'] < $pageWith)
{
echo "<span><img height='".$Image['height']."' width='".$Image['width']."' src='/ite1".$Image['url']."' style='padding:2px;'></span>";
$pageWith = $pageWith - $Image['width'];
}
else
{
echo "<br>";
$pageWith = 120;
$pageHeight = $pageHeight - $Image['height'];
echo "<span><img height='".$Image['height']."' width='".$Image['width']."' src='/ite1".$Image['url']."' style='padding:2px;'></span>";
$pageWith = $pageWith - $Image['width'];
};
}
//If there's no more space on the page because we're at the end of the page, create a new one
else
{
echo "</div id='pageFrame'>";
echo "<div id='pageFrame' style='width:120px; height:180px;'>";
$pageHeight = 180;
};
};
echo "</div id='pageFrame'>";
Can you advise?
I'm generating an invoice PDF using laravel snappy wkhtmltopdf
and I'm tring to add some text in the bottom of the last page,
now i already have a footer-html with the number of the page.
I tried to show the content only in the last page with this way:
<!DOCTYPE html>
<div id="footer_text">
{!! nl2br($footer_text) !!}
</div>
<div class="pagination"><span id='page'></span> of <span id='topage'></span></div>
<script>
function remove(id) {
var elem = document.getElementById(id);
return elem.parentNode.removeChild(elem);
}
var vars={};
var x=window.location.search.substring(1).split('&');
for (var i in x) {
var z=x[i].split('=',2);
vars[z[0]] = unescape(z[1]);
}
document.getElementById('page').innerHTML = vars.page;
document.getElementById('topage').innerHTML = vars.topage;
if( vars.page != vars.topage && vars.topage > 1){
document.getElementById('footer_text').innerHTML = '';
remove('footer_text');
}
if(vars.topage == 1){
document.getElementById('pages').innerHTML = '';
}
</script>
and it does show me the text only in the last page BUT in the previous pages I have a big white space, here is a screenshot:
page number 1:
page number 2:
I feel like i tried everything, please help me
There is no issue with your script it might be some style issue. As you are removing footer_text in all previous pages and showing only on last page and this is somehow creating too much space. Check your CSS there must be margin-bottom or padding-bottom which is creating too much space. Enjoy!
Late to the party but looks like the ID footer_text will be set multiple times and ID's should be unique so I guess it would have worked if you used a class instead and getElementsByClassName
The footer height can't be dynamic on a different page when using Wkhtmltopdf. It's always with a static height. If this is your footer.html you have to add to your style:
body {height: 70px /for example/; position: relative;}
so you can align bottom (with position:absolute; bottom:0;) you #footer_text and content. But still, have some white space on all prev pages.
In the PDF generators, the footer content area is independent of the body content.
Okay so I have this portfolio page where I display a couple of thumbnails, and you can order it by tags, so for example like this:
year 1
And this works fine. However, my thumbnails display at three on a row, so only the first two should have a right margin, the third one no margin.
I used PHP to do this which works fine.
if ($result=$link->query($query)) {
for ($i=1; $i <= $result->num_rows; $i++) {
$row= $result->fetch_assoc();
$id = $row['number'];
$title = $row['title'];
$bgthumbnail = $row['thumbnail'];
if($i%3 == 0){
echo "
<div class=\"thumbnail\">
<a href=\"portfoliodetail.php?id=$id\">
<div class=\"thumbnailOverview noMargin\" style=\"background: url('images/portfolio/thumbnails/$bgthumbnail'); background-position: center center;\">
<div class=\"latestWorkTitle\">$title</div>
</div>
</a>
</div>
";
} else {
echo "
<div class=\"thumbnail\">
<a href=\"portfoliodetail.php?id=$id\">
<div class=\"thumbnailOverview\" style=\"background: url('images/portfolio/thumbnails/$bgthumbnail'); background-position: center center;\">
<div class=\"latestWorkTitle\">$title</div>
</div>
</a>
</div>
";
}
}
$result->close();
}
However, when I click a tag, the margin doesn't update. So when a thumbnail was given no margin in the overview because it was the third one in row, when it displays first because of a chosen tag, it also receives no margin.
Of course this is because nothing "refreshes" or something, but I was wondering if there is an "easy" way to fix this problem? To make the PHP loop run again or something?
You must to set/remove noMargin class name via javascript:
$('.year-clicker').click(function (event) {
event.preventDefault();
var year = $(event.currentTarget).data('year');
$('.thumb').hide().removeClass('noMargin').filter('.year' + year).show();
$('.thumb:visible').each(function (i, e) {
if ((i + 1) % 3 == 0) {
$(e).addClass('noMargin');
}
});
return false;
});
Try out this jsfiddle http://jsfiddle.net/xgE3K/1/
unless your "tags" are recalling the page - so that the php is re-executed - you probably want to look at javascript (or possibly ajax) to do the reformatting of the layout.
Depending on the quantity of thumbnails and the variety of tags, you might use the php to create a div (with a relevant id and a style="" attribute) for each of the different filter tags - containing the layout of the thumbnails for that tag (so you can ensure your layout is fine for each view).
i.e. repeat your code above enclosed by a unique div tag for each view.
Make the default view div visible (style="display: block") and the others hidden (style="dsplay: none").
Then have a javascript function that is executed on any tag click. This will make the relevant div visible and the rest hidden, by changing their style value as above.
Uses a bit more memory, but your switching between views will be quicker than doing a reload.
Despite all this, I think it's cleaner and more scalable to recall the page with the relative filter (depending on the tag) then you will have more control over the layout.
hi i wonder if anyone can help, not entirely sure this is possible but i have a div container called <div class="scroll">
inside div scroll is a sql query that echoes comments posted by users from the database. the div scroll is set to a width of 600px and so the comments will be arranged in this div left to right, theres two comment boxes on each line, each comment box is just under 300px each as to align next to each other.
the comments are listed like so:
<div class="scroll">
comment 1 | comment 2
comment 3 | comment 4
comment 5 | comment 6
</div>
the comments are encased in the div "comment_box"
now what i have done is put a background image to the div "comment_box" this image is a pointer arrow that points to the left and is positioned on the left hand side of the div, but i also want to have a second background image for the comment boxes that align on the right, so in this instance comments 2, 4 and 6 will have a different background image/an arrow that points to the right on the right hand side of the div.
is this possible?
thanks
comment box{
.wall_post_case_branch {
background-image:url(../img/effects/arrow_left.png);
background-repeat:no-repeat;
background-position: center;
height:90px;
width:290px;
position:relative;
border:#ccc 1px solid;
}
mysql:
<?php
if(mysql_num_rows($wallposts_set) > 0) {
while ($posts = mysql_fetch_array($wallposts_set)) {
$age = days_from_date($posts['date_added']);
?>
<div class="comment_box">
<?php echo "{$posts['content']}"; ?>
</div>
You can do it as below.
<?php
$i = 0;
while ($posts = mysql_fetch_array($wallposts_set))
{
$class = ' odd';
if ($i++ % 2 == 0)
{
$class = ' even';
}
echo '<div class="comment_box'.$class.'">';
}
?>
and in css
.odd { background-image:imageone.jpg; }
.even{ background-image:imagesecond.jpg; }
I would right before the while loop make the variable $i = 0;.
It would use that to define what background color it should use.
Then in the while loop I would use this code:
while ($posts = mysql_fetch_array($wallposts_set)) {
$i++;
$age = days_from_date($posts['date_added']);
echo "<div class=\"comment_box_$i\">
$posts['content']}
</div>";
if ($i == 2)
$i = 0;
}
Then the boxes will have 2 difference classes, the one of the left will have the class="comment_box_1" while the right box will have class="comment_box_2".
Then you can create your CSS for the difference boxes.
Enjoy
You can also do this simply with CSS by using:
:nth-child(even)
:nth-child(odd)
The fiddle illustrates with background colors instead of images.
http://jsfiddle.net/ingvi/CFMcA/
hope it helps
So let's say that I have a number of images on my server, uploaded by users. Each picture also has a row in the database containing information like filename, category, filesize, extension and location.
When I want to show these images I use a mysql_query and a while loop. This works, but the pictures all have different sizes and they show up next to each other or under each other which looks really bad.
Let's asume that I have 20 of these images, how can I show 5 images next to each other, 4 rows long???
Assuming a numerically indexed array of objects containing image data:
<div class="row">
<?php
// loop through all my images taking the index and image object
foreach( $images as $idx=>$image ) {
// If this isn't the 1st image & the modulus of 5 and $idx is 0, make a new row.
if( $idx > 0 && !(5%$idx) ) {
</div><div class="row">
}
// Print out the image
echo '<img src="' . $image->filename . '" />';
} ?>
</div>
The modulus is basically the remainder if you divide the first number by the second. Therefore when we've printed 5 images, 5/5=1 with no remainder, so we create a new row.
I believe that would create something like:
<div class="row">
<img src="img1.jpg" />
<img src="img2.jpg" />
<img src="img3.jpg" />
<img src="img4.jpg" />
<img src="img5.jpg" />
</div>
<div class="row">
<img src="img6.jpg" />
<img src="img7.jpg" />
<img src="img8.jpg" />
<img src="img9.jpg" />
<img src="img10.jpg" />
</div>
Which could then be styled using CSS:
<style type="text/css">
div.row {
clear:left;
}
div.row img {
float:left;
}
</style>
When it comes to resizing the images, you have a 2 options:
The easy option: set the image width in the CSS (the height will scale accordingly). This is less than perfect and if the images are different aspect ratios (width vs. height), will still look messy.
Resize (and crop if necessary) using a PHP library such as GD or imagick (a PHP layer on top of imagemagick). Personally I prefer GD as it's packaged with PHP so it's got wider support. That said, imagick is quicker, simpler code, so it's up to you. This site gives a good comparison between the two for image resizing.
EDIT: in answer to your question, you'd need to do something like this to get the array of images:
<?php
// Build an array of images
$sql = 'SELECT * FROM images';
$result = mysql_query( $sql );
while( $obj = mysql_fetch_object( $result ) ) {
$images[] = $obj;
}
?>
pseudocode
$i = 0;
while($row = mysql_fetch_object($result) {
//displaying image
....
if($i % 4 == 0) {
echo '<BR>';
}
$i++;
}