Creating irregular crop window on image using Javascript
Look at the project structure below, I have only one image under asset folder for testing, under 'libs' folder I have d3 library and numeric library
I am using d3 library to make the crop window and other transition effects and numeric library to calculate positions
So lets look at the code...
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Image Crop</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<div class="container">
<div id="background" class="o_image">
<img src="./assets/bill.png" alt="bill" />
<span>©reactcodes blog</span>
</div>
</div>
</body>
<script src="./libs/d3.v3.min.js"></script>
<script src="./libs/numeric-solve.min.js"></script>
<script src="./index.js"></script>
<script src="./crop_window.js"></script>
</html>
index.js
const attachCropBox = function (imgWidth,imgHeight) {
console.log('image loaded : ',imgWidth ,' ', imgHeight)
var margin = {top: 40, right: 40, bottom: 40, left: 40},
width = imgWidth - margin.left - margin.right,
height = imgHeight - margin.top - margin.bottom;
var sourcePoints = [[0, 0], [width, 0], [width, height], [0, height]],
targetPoints = [[0, 0], [width, 0], [width, height], [0, height]];
var svg = d3.select("#background").append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")")
.attr("id","window_g");
var line = svg.selectAll(".line")
.data(d3.range(0, width + 1, 40).map(function(x) { return [[x, 0], [x, height]]; })
.concat(d3.range(0, height + 1, 40).map(function(y) { return [[0, y], [width, y]]; })))
.enter().append("path")
.attr("class", "line line--x");
var handle = svg.selectAll(".handle")
.data(targetPoints)
.enter().append("circle")
.attr("class", "handle")
.attr("transform", function(d) { return "translate(" + d + ")"; })
.attr("r", 7)
.call(d3.behavior.drag()
.origin(function(d) { return {x: d[0], y: d[1]}; })
.on("drag", dragged));
d3.selectAll("button")
.datum(function(d) { return JSON.parse(this.getAttribute("data-targets")); })
.on("click", clicked)
.call(transformed);
function clicked(d) {
d3.transition()
.duration(750)
.tween("points", function() {
if (!(d == null)){
var i = d3.interpolate(targetPoints, d);
return function(t) {
handle.data(targetPoints = i(t)).attr("transform", function(d) { return "translate(" + d + ")"; });
transformed();
};
}
});
}
function dragged(d) {
d3.select(this).attr("transform", "translate(" + (d[0] = d3.event.x) + "," + (d[1] = d3.event.y) + ")");
transformed();
}
function transformed() {
for (var a = [], b = [], i = 0, n = sourcePoints.length; i < n; ++i) {
var s = sourcePoints[i], t = targetPoints[i];
a.push([s[0], s[1], 1, 0, 0, 0, -s[0] * t[0], -s[1] * t[0]]), b.push(t[0]);
a.push([0, 0, 0, s[0], s[1], 1, -s[0] * t[1], -s[1] * t[1]]), b.push(t[1]);
}
var X = solve(a, b, true), matrix = [
X[0], X[3], 0, X[6],
X[1], X[4], 0, X[7],
0, 0, 1, 0,
X[2], X[5], 0, 1
].map(function(x) {
return d3.round(x, 6);
});
line.attr("d", function(d) {
return "M" + project(matrix, d[0]) + "L" + project(matrix, d[1]);
});
}
function project(matrix, point) {
point = multiply(matrix, [point[0], point[1], 0, 1]);
return [point[0] / point[3], point[1] / point[3]];
}
function multiply(matrix, vector) {
return [
matrix[0] * vector[0] + matrix[4] * vector[1] + matrix[8 ] * vector[2] + matrix[12] * vector[3],
matrix[1] * vector[0] + matrix[5] * vector[1] + matrix[9 ] * vector[2] + matrix[13] * vector[3],
matrix[2] * vector[0] + matrix[6] * vector[1] + matrix[10] * vector[2] + matrix[14] * vector[3],
matrix[3] * vector[0] + matrix[7] * vector[1] + matrix[11] * vector[2] + matrix[15] * vector[3]
];
}
}
window.onload = function() {
var img = new Image();
img.onload = function () {
const imgWidth = img.width;
const imgHeight = img.height;
attachCropBox(imgWidth,imgHeight)
}
img.src = "./assets/bill.png";
}
style.css
body{
margin:0;
padding: 0;
}
.container{
display: flex;
}
.container .o_image{
width: 50%;
}
.container .p_image{
width: 50%;
text-align: center;
}
span{
display: block;
}
img{
border: 1px solid blue;
}
.b_container{
text-align: center;
width: 100%;
}
button{
width: 80px;
height: 30px;
border: none;
background-color: coral;
}
svg {
position: absolute;
top: 0;
left: 0;
}
.line {
stroke: blue;
stroke-width: 0.7px;
stroke-linecap: square;
}
.handle {
fill: blue;
pointer-events: all;
stroke:blue;
stroke-width: 2px;
cursor: move;
opacity: 0.8;
}
Thanks for visiting my blog... have a nice day

What a very good article. I appreciate your post.We are an PHOTO CROPPING Company. Recently we have post an article about add brushes to photoshop and Vehicles Background Replacement. Please check out. Thanks
ReplyDelete