I am currently using MapCenterCoord plugin for my leaflet project https://github.com/xguaita/Leaflet.MapCenterCoord
The plugin works fine but I am trying to extract the coordinates it produced as a value (and to show it on console.log() so I can reuse it for placing a marker on the map).
However, I cannot for the life of me figure out how to get the value out of the plugin. Anyone got any tips?
This is the script (I have tried placing console.log in onAdd as console.log(this._getMapCenterCoord()) but to no effect, as it only gives the current GPS onload rather than updated on the console whenever I move the window):
L.Control.MapCenterCoord = L.Control.extend({
// Defaults
options: {
position: 'bottomleft',
icon: true,
onMove: false,
template: '{y} | {x}', // https://en.wikipedia.org/wiki/ISO_6709
projected: false,
formatProjected: '#.##0,000',
latlngFormat: 'DD', // DD, DM, DMS
latlngDesignators: false,
latLngFormatter: undefined
},
onAdd: function (map) {
if (this.options.icon) {
// create a DOM element and put it into overlayPane
this._iconEl = L.DomUtil.create('div', 'leaflet-control-mapcentercoord-icon leaflet-zoom-hide');
map.getPanes().overlayPane.appendChild(this._iconEl);
// add a viewreset event listener for updating icon's position
map.on('viewreset', this._onReset, this);
this._onReset();
}
// Control container
this._container = L.DomUtil.create('div', 'leaflet-control-mapcentercoord');
L.DomEvent.disableClickPropagation(this._container);
// Add events listeners for updating coordinates & icon's position
map.on('move', this._onMapMove, this);
map.on('moveend', this._onMapMoveEnd, this);
this._container.innerHTML = this._getMapCenterCoord();
return this._container;
},
onRemove: function (map) {
// remove icon's DOM elements and listeners
if (this.options.icon) {
map.getPanes().overlayPane.removeChild(this._iconEl);
map.off('viewreset', this._onReset, this);
}
map.off('move', this._onMapMove, this);
map.off('moveend', this._onMapMove, this);
},
_onReset: function (e) {
// update icon's position
var pos = this._map.latLngToLayerPoint(this._map.getCenter());
L.DomUtil.setPosition(this._iconEl, pos);
},
_onMapMove: function (e) {
if (this.options.icon) {
// update icon's position
var pos = this._map.latLngToLayerPoint(this._map.getCenter());
L.DomUtil.setPosition(this._iconEl, pos);
}
if (this.options.onMove) {
// update coordinates
this._container.innerHTML = this._getMapCenterCoord();
}
},
_onMapMoveEnd: function (e) {
if (this.options.icon) {
// update icon's position
var pos = this._map.latLngToLayerPoint(this._map.getCenter());
L.DomUtil.setPosition(this._iconEl, pos);
}
// update coordinates
this._container.innerHTML = this._getMapCenterCoord();
},
_getMapCenterCoord: function () {
if (this.options.projected) return this._getProjectedCoord(this._map.options.crs.project(this._map.getCenter()));
return this._getLatLngCoord(this._map.getCenter());
},
_getProjectedCoord: function (center) {
return L.Util.template(this.options.template, {
x: this._format(this.options.formatProjected, center.x),
y: this._format(this.options.formatProjected, center.y)
});
},
_getLatLngCoord: function (center) {
if (this.options.latLngFormatter != undefined) return this.options.latLngFormatter(center.lat, center.lng);
var lat, lng, deg, min;
//make a copy of center so we aren't affecting leaflet's internal state
var centerCopy = {
lat: center.lat,
lng: center.lng
};
// 180 degrees & negative
if (centerCopy.lng < 0) {
centerCopy.lng_neg = true;
centerCopy.lng = Math.abs(centerCopy.lng);
} else centerCopy.lng_neg = false;
if (centerCopy.lat < 0) {
centerCopy.lat_neg = true;
centerCopy.lat = Math.abs(centerCopy.lat);
} else centerCopy.lat_neg = false;
if (centerCopy.lng > 180) {
centerCopy.lng = 360 - centerCopy.lng;
centerCopy.lng_neg = !centerCopy.lng_neg;
}
// format
if (this.options.latlngFormat === 'DM') {
deg = parseInt(centerCopy.lng);
lng = deg + 'º ' + this._format('00.000', (centerCopy.lng - deg) * 60) + "'";
deg = parseInt(centerCopy.lat);
lat = deg + 'º ' + this._format('00.000', (centerCopy.lat - deg) * 60) + "'";
} else if (this.options.latlngFormat === 'DMS') {
deg = parseInt(centerCopy.lng);
min = (centerCopy.lng - deg) * 60;
lng = deg + 'º ' + this._format('00', parseInt(min)) + "' " + this._format('00.0', (min - parseInt(min)) * 60) + "''";
deg = parseInt(centerCopy.lat);
min = (centerCopy.lat - deg) * 60;
lat = deg + 'º ' + this._format('00', parseInt(min)) + "' " + this._format('00.0', (min - parseInt(min)) * 60) + "''";
} else { // 'DD'
lng = this._format('#0.00000', centerCopy.lng) + 'º';
lat = this._format('##0.00000', centerCopy.lat) + 'º';
}
return L.Util.template(this.options.template, {
x: (!this.options.latlngDesignators && centerCopy.lng_neg ? '-' : '') + lng + (this.options.latlngDesignators ? (centerCopy.lng_neg ? ' W' : ' E') : ''),
y: (!this.options.latlngDesignators && centerCopy.lat_neg ? '-' : '') + lat + (this.options.latlngDesignators ? (centerCopy.lat_neg ? ' S' : ' N') : '')
});
},
_format: function (m, v) {
if (!m || isNaN(+v)) {
return v; //return as it is.
}
//convert any string to number according to formation sign.
var v = m.charAt(0) == '-' ? -v : +v;
var isNegative = v < 0 ? v = -v : 0; //process only abs(), and turn on flag.
//search for separator for grp & decimal, anything not digit, not +/- sign, not #.
var result = m.match(/[^\d\-\+#]/g);
var Decimal = (result && result[result.length - 1]) || '.'; //treat the right most symbol as decimal
var Group = (result && result[1] && result[0]) || ','; //treat the left most symbol as group separator
//split the decimal for the format string if any.
var m = m.split(Decimal);
//Fix the decimal first, toFixed will auto fill trailing zero.
v = v.toFixed(m[1] && m[1].length);
v = +(v) + ''; //convert number to string to trim off *all* trailing decimal zero(es)
//fill back any trailing zero according to format
var pos_trail_zero = m[1] && m[1].lastIndexOf('0'); //look for last zero in format
var part = v.split('.');
//integer will get !part[1]
if (!part[1] || part[1] && part[1].length <= pos_trail_zero) {
v = (+v).toFixed(pos_trail_zero + 1);
}
var szSep = m[0].split(Group); //look for separator
m[0] = szSep.join(''); //join back without separator for counting the pos of any leading 0.
var pos_lead_zero = m[0] && m[0].indexOf('0');
if (pos_lead_zero > -1) {
while (part[0].length < (m[0].length - pos_lead_zero)) {
part[0] = '0' + part[0];
}
} else if (+part[0] == 0) {
part[0] = '';
}
v = v.split('.');
v[0] = part[0];
//process the first group separator from decimal (.) only, the rest ignore.
//get the length of the last slice of split result.
var pos_separator = (szSep[1] && szSep[szSep.length - 1].length);
if (pos_separator) {
var integer = v[0];
var str = '';
var offset = integer.length % pos_separator;
for (var i = 0, l = integer.length; i < l; i++) {
str += integer.charAt(i); //ie6 only support charAt for sz.
//-pos_separator so that won't trail separator on full length
if (!((i - offset + 1) % pos_separator) && i < l - pos_separator) {
str += Group;
}
}
v[0] = str;
}
v[1] = (m[1] && v[1]) ? Decimal + v[1] : "";
return (isNegative ? '-' : '') + v[0] + v[1]; //put back any negation and combine integer and fraction.
}
});
L.Map.mergeOptions({
MapCenterCoordControl: false
});
L.Map.addInitHook(function () {
if (this.options.MapCenterCoordControl) {
this.MapCenterCoordControl = new L.Control.MapCenterCoord();
this.addControl(this.MapCenterCoordControl);
}
});
L.control.mapCenterCoord = function (options) {
return new L.Control.MapCenterCoord(options);
};
Below is an example of how to easily determine the center of the map. This is a reworked one of my examples so it's more goodies but you can simplify yourself.
/* eslint-disable no-undef */
/**
* Obtaining coordinates of the visible map
*/
// config map
let config = {
minZoom: 7,
maxZomm: 18,
};
// magnification with which the map will start
const zoom = 18;
// co-ordinates
const lat = 52.22977;
const lng = 21.01178;
// calling map
const map = L.map('map', config).setView([lat, lng], zoom);
// Used to load and display tile layers on the map
// Most tile servers require attribution, which you can set under `Layer`
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
attribution: '© OpenStreetMap contributors',
}).addTo(map);
const markerPlace = document.querySelector('.marker-position');
// on drag end
map.on('dragend', setRentacle);
// second option, by dragging the map
map.on('dragstart', updateInfo);
// on zoom end
map.on('zoomend', setRentacle);
// update info about bounds when site loaded
document.addEventListener('DOMContentLoaded', function() {
const bounds = map.getBounds();
updateInfo(bounds._northEast, bounds._southWest);
});
// set rentacle function
function setRentacle() {
const bounds = map.getBounds();
// update info about bounds
updateInfo(bounds._northEast, bounds._southWest);
// set rentacle
L.rectangle(bounds, {
color: randomColor(),
weight: 20,
fillOpacity: 0.1,
}).addTo(map);
// set map
map.fitBounds(bounds);
}
// generate random color
function randomColor() {
return `#${Math.floor(Math.random() * 16777215).toString(16)}`;
}
function updateInfo(north, south) {
const center = map.getCenter();
markerPlace.textContent =
south === undefined ?
'We are moving the map...' :
`Center: ${center}, SouthWest: ${north}, NorthEast: ${south}`;
}
#import url(https://fonts.googleapis.com/css?family=Lato&display=swap);
*,
:after,
:before {
box-sizing: border-box;
padding: 0;
margin: 0;
}
html {
height: 100%;
}
body,
html,
#map {
width: 100%;
height: 100%;
margin: 0;
padding: 0;
}
#map:before {
position: absolute;
content: "+";
font-size: 3rem;
font-weight: 400;
display: flex;
justify-content: center;
align-items: center;
color: red;
z-index: 9999999;
top: 0;
left: 0;
right: 0;
bottom: 0;
margin: auto;
border: 10px;
}
body {
position: relative;
height: 100%;
font-family: Lato, sans-serif;
min-height: 100%;
padding: 0;
margin: 0;
}
.marker-position {
position: absolute;
bottom: 0;
left: 0;
z-index: 999;
padding: 20px 10px;
font-size: 10px;
background-color: #fff;
}
<link rel="stylesheet" href="https://unpkg.com/leaflet#1.6.0/dist/leaflet.css" />
<script src="https://unpkg.com/leaflet#1.6.0/dist/leaflet.js"></script>
<div class="marker-position"></div>
<div id="map"></div>
As for your problem, console.log is working. I tested locally and the coordinates appear.
onAdd: function(map) {
...
this._container.innerHTML = this._getMapCenterCoord();
// works
console.log(this._getMapCenterCoord());
return this._container;
},
_onMapMoveEnd: function(e) {
...
// update coordinates
this._container.innerHTML = this._getMapCenterCoord();
// works
console.log(this._getMapCenterCoord());
},
Related
I have this little block that I move around using javascript code. It works all good except if I keep moving it, it can easily get out of the box where it is supposed to be.
Can I prevent this somehow? So no matter how far I want to move it, it will stay stuck inside of the container/box ?
Here's my snippet code:
/// store key codes and currently pressed ones
var keys = {};
keys.UP = 38;
keys.LEFT = 37;
keys.RIGHT = 39;
keys.DOWN = 40;
/// store reference to character's position and element
var character = {
x: 100,
y: 100,
speedMultiplier: 2,
element: document.getElementById("character")
};
var is_colliding = function(div1, div2) {
var d1_height = div1.offsetHeight;
var d1_width = div1.offsetWidth;
var d1_distance_from_top = div1.offsetTop + d1_height;
var d1_distance_from_left = div1.offsetLeft + d1_width;
var d2_height = div2.offsetHeight;
var d2_width = div2.offsetWidth;
var d2_distance_from_top = div2.offsetTop + d2_height;
var d2_distance_from_left = div2.offsetLeft + d2_width;
var not_colliding =
d1_distance_from_top <= div2.offsetTop ||
div1.offsetTop >= d2_distance_from_top ||
d1_distance_from_left <= div2.offsetTop ||
div1.offsetLeft >= d2_distance_from_left;
return !not_colliding;
};
/// key detection (better to use addEventListener, but this will do)
document.body.onkeyup =
document.body.onkeydown = function(e){
if (e.preventDefault) {
e.preventDefault();
}
else {
e.returnValue = false;
}
var kc = e.keyCode || e.which;
keys[kc] = e.type == 'keydown';
};
/// character movement update
var moveCharacter = function(dx, dy){
character.x += (dx||0) * character.speedMultiplier;
character.y += (dy||0) * character.speedMultiplier;
character.element.style.left = character.x + 'px';
character.element.style.top = character.y + 'px';
};
/// character control
var detectCharacterMovement = function(){
if ( keys[keys.LEFT] ) {
moveCharacter(-5, 0);
}
if ( keys[keys.RIGHT] ) {
moveCharacter(5, 0);
}
if ( keys[keys.UP] ) {
moveCharacter(0, -5);
}
if ( keys[keys.DOWN] ) {
moveCharacter(0, 5);
}
};
/// update current position on screen
moveCharacter();
/// game loop
setInterval(function(){
detectCharacterMovement();
}, 1000/24);
body{
display: flex;
justify-content: center;
align-items: center;
}
#character {
position: absolute;
width: 42px;
height: 42px;
background: red;
z-index:99;
}
#container{
width: 400px;
height: 400px;
background: transparent;
border:5px solid rgb(0, 0, 0);
position: relative;
overflow: hidden;
}
<div id="container">
<div id="character"></div>
</div>
PS: You can move the box using keyboard arrows.
Get the container width and height into variable and set a condition on your move
var moveCharacter = function(dx, dy){
let div_width = document.getElementById('container').clientWidth;
let div_height = document.getElementById('container').clientHeight;
if((div_width - character.x) < 50 ){ // 50 = width of character and padding
character.x = div_width - 50;
}
if(character.x < 10){ // Padding
character.x = 11;
}
if((div_height - character.y) < 50 ){
character.y = div_height - 50;
}
if(character.y < 10){
character.y = 11;
}
I am currently trying to generate a View with React Typescript where I can show all Appointments of a day (similar to outlook calendar). But I am facing an issue. When Appointments are overlapping how can I determine the with and position? So I guess that I probably need an algorithm accomplish this issue.
Here an example how it could be structured:
Every box stands for an Appointment
The classes of the Object looks like this:
class AppointmentStructured {
appointment: Appointment
width?: number
left?: number
}
class Appointment {
start: Date
end: Date
subject:string
duration: number
}
I am looking for a solution where I can determine the width (max. 1 = 100%) of the specific appointment and additionally I need the position. The easiest would be to how far it is from the left (max. 1). When there are more than 2 appointments starting at the same time, the appointments are ordered by the appointment with the longest duration.
Example the third box from the image would be:
width: 0.333
left: 0.666
Out of this data I can use CSS to design them.
The generated CSS then would look more or less like this:
position: absolute;
width: calc(100% * 0.33333);
top: 20rem; //doesn't matter for the algorithm
left: calc(100% * 0.66666)
--Edit
This is how far i came. It starts with the method generateAppointmentTile([])
export interface AppointmentStructured {
appointment: Appointment
width: string | number;
left: string | number
}
export const generateAppointmentTile = (appointments: Appointment[]): AppointmentStructured[] => {
var appointmentsStructured = initAppointmentStructured(appointments);
var response: AppointmentStructured[] = [];
for (var i = 0; i < appointmentsStructured.length; ++i) {
var width = 1;
var previous = getPreviousOverlapping(appointmentsStructured, i);
var previousWidth = previous
.map((item: AppointmentStructured): number => item.width as number)
.reduce((a: number, b: number): number => a + b, 0)
var forward = getForwardOverlapping(appointmentsStructured, i);
var forwardOverlays = appointmentOverlays(forward);
var previousHasOverlayWithForward = false;
previous.forEach((structured: AppointmentStructured) => checkAppointmentOverlaySingle(structured, forward) !== 0 ? previousHasOverlayWithForward = true : null);
width = (width - previousWidth) / (!previousHasOverlayWithForward && forwardOverlays !== 0 ? forwardOverlays : 1);
appointmentsStructured[i].width = width;
response.push(appointmentsStructured[i]);
}
response.forEach((value: AppointmentStructured): void => {
value.width = `calc((100% - 8rem) * ${value.width})`;
});
return response
}
const appointmentOverlays = (reading: AppointmentStructured[]): number => {
var highestNumber = 0;
reading.forEach((structured: AppointmentStructured): void => {
var start = checkAppointmentOverlaySingle(structured, reading) + 1;
highestNumber = start > highestNumber ? start : highestNumber;
});
return highestNumber;
}
const checkAppointmentOverlaySingle = (structured: AppointmentStructured, reading: AppointmentStructured[]): number => {
var start = 0;
reading.forEach((item: AppointmentStructured): void => {
if (item.appointment.id !== structured.appointment.id) {
if ((structured.appointment.start <= item.appointment.start && structured.appointment.end >= item.appointment.start)
|| (structured.appointment.start >= item.appointment.start && structured.appointment.start <= item.appointment.end)) {
start += 1;
}
}
});
return start;
}
const getPreviousOverlapping = (appointmentsStructured: AppointmentStructured[], index: number): AppointmentStructured[] => {
var response: AppointmentStructured[] = [];
for (var i = index - 1; i >= 0; --i) {
if (appointmentsStructured[index].appointment.start >= appointmentsStructured[i].appointment.start
&& appointmentsStructured[index].appointment.start <= appointmentsStructured[i].appointment.end) {
response.push(appointmentsStructured[i]);
}
}
return response;
}
const getForwardOverlapping = (appointmentsStructured: AppointmentStructured[], index: number): AppointmentStructured[] => {
var response: AppointmentStructured[] = [];
for (var i = index; i < appointmentsStructured.length; ++i) {
if (appointmentsStructured[index].appointment.start >= appointmentsStructured[i].appointment.start
&& appointmentsStructured[index].appointment.start <= appointmentsStructured[i].appointment.end) {
response.push(appointmentsStructured[i]);
}
}
return response;
}
const initAppointmentStructured = (appointments: Appointment[]): AppointmentStructured[] => {
var appointmentsStructured: AppointmentStructured[] = appointments
.sort((a: Appointment, b: Appointment): number => a.start.getTime() - b.start.getTime())
.map((appointment: Appointment): AppointmentStructured => ({ appointment, width: 100, left: 0 }));
var response: AppointmentStructured[] = [];
// sort in a intelligent way
for (var i = 0; i < appointmentsStructured.length; ++i) {
var duration = appointmentsStructured[i].appointment.end.getTime() - appointmentsStructured[i].appointment.start.getTime();
var sameStartAppointments = findAppointmentWithSameStart(appointmentsStructured[i], appointmentsStructured);
var hasLongerAppointment: boolean = false;
sameStartAppointments.forEach((structured: AppointmentStructured) => (structured.appointment.end.getTime() - structured.appointment.start.getTime()) > duration ? hasLongerAppointment = true : null);
if (!hasLongerAppointment) {
response.push(appointmentsStructured[i]);
appointmentsStructured.splice(i, 1);
i = -1;
}
}
return response.sort((a: AppointmentStructured, b: AppointmentStructured): number => a.appointment.start.getTime() - b.appointment.start.getTime());
}
const findAppointmentWithSameStart = (structured: AppointmentStructured, all: AppointmentStructured[]): AppointmentStructured[] => {
var response: AppointmentStructured[] = [];
all.forEach((appointmentStructured: AppointmentStructured) => appointmentStructured.appointment.start === structured.appointment.start
&& appointmentStructured.appointment.id !== structured.appointment.id ? response.push(appointmentStructured) : null)
return response;
}
Even some pseudo code would help me a lot.
This solution is based on this answer: Visualization of calendar events. Algorithm to layout events with maximum width
I've just ported the script from C# to JS and extracted some steps.
Group all your intervals/appointments into distinct groups, where one group does not influence another.
For each group, fill columns from left to right (with each appointment getting the same width). Just use as many columns as you need.
Stretch each appointment maximally.
Make the actual boxes from that and render them.
This procedure is not perfect. Sometimes you will have a thin box on the left and the box to the right got stretched all the way. This happens mainly when the left box has a long "overlap chain" to the bottom, which does not really happen in practice.
/*
interface Interval {
start: number;
end: number;
}
*/
// Just for demonstration purposes.
function generateIntervals({ count, maxStart, maxEnd, minLength, maxLength, segments }) {
return Array.from(Array(count), () => randomInterval())
function randomInterval() {
const start = randomInt(0, maxStart * segments) / segments
const length = randomInt(minLength * segments, maxLength * segments) / segments
return {
start,
end: Math.min(start + length, maxEnd),
text: "Here could be your advertising :)"
}
}
function randomInt(start, end) {
return Math.floor(Math.random() * (end - start) + start)
}
}
function isOverlapping(interval1, interval2) {
const start = Math.max(interval1.start, interval2.start)
const end = Math.min(interval1.end, interval2.end)
return start < end
}
// Sorts by start ascending.
function sortIntervalsByStart(intervals) {
return intervals
.slice()
.sort(({ start: s1 }, { start: s2 }) => s1 - s2)
}
// Split intervals into groups, which are independent of another.
function groupIntervals(intervals) {
const groups = []
let latestIntervalEnd = -Infinity
for (const interval of sortIntervalsByStart(intervals)) {
const { start, end } = interval
// There is no overlap to previous intervals. Create a new group.
if (start >= latestIntervalEnd) {
groups.push([])
}
groups[groups.length - 1].push(interval)
latestIntervalEnd = Math.max(latestIntervalEnd, end)
}
return groups
}
// Fill columns with equal width from left to right.
function putIntervalsIntoColumns(intervals) {
const columns = []
for (const interval of intervals) {
let columnIndex = findFreeColumn(interval)
columns[columnIndex] = (columns[columnIndex] || []).concat([interval])
}
return columns
function findFreeColumn(interval) {
let columnIndex = 0
while (
columns?.[columnIndex]
?.some(otherInterval => isOverlapping(interval, otherInterval))
) {
columnIndex++
}
return columnIndex
}
}
// Expand columns maximally.
function makeBoxes(columns, containingInterval) {
const boxes = []
for (let columnIndex = 0; columnIndex < columns.length; columnIndex++) {
for (const interval of columns[columnIndex]) {
const columnSpan = findColumnSpan(columnIndex, interval)
const box = {
...interval,
top: (interval.start - containingInterval.start) / (containingInterval.end - containingInterval.start),
height: (interval.end - interval.start) / (containingInterval.end - containingInterval.start),
left: columnIndex / columns.length,
width: columnSpan / columns.length
}
boxes.push(box)
}
}
return boxes
function findColumnSpan(columnIndex, interval) {
let columnSpan = 1
while (
columns
?.[columnIndex + columnSpan]
?.every(otherInterval => !isOverlapping(interval, otherInterval))
) {
columnSpan++
}
return columnSpan
}
}
function computeBoxes(intervals, containingInterval) {
return groupIntervals(intervals)
.map(intervals => putIntervalsIntoColumns(intervals))
.flatMap(columns => makeBoxes(columns, containingInterval))
}
function renderBoxes(boxes) {
const calendar = document.querySelector("#calendar")
for (const { top, left, width, height, text = "" } of boxes) {
const el = document.createElement("div")
el.style.position = "absolute"
el.style.top = `${top * 100}%`
el.style.left = `${left * 100}%`
el.style.width = `${width * 100}%`
el.style.height = `${height * 100}%`
el.textContent = text
calendar.appendChild(el)
}
}
const hours = 24
const intervals = generateIntervals({
count: 20,
maxStart: 22,
maxEnd: hours,
maxLength: 4,
minLength: 1,
segments: 2,
})
const boxes = computeBoxes(intervals, { start: 0, end: hours })
renderBoxes(boxes)
* {
box-sizing: border-box;
}
body {
margin: 0;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
}
#calendar {
height: 100vh;
width: 100vw;
min-height: 800px;
position: relative;
}
#calendar > * {
border: 1px solid black;
background-color: #b89d9d;
display: flex;
justify-content: center;
align-items: center;
text-align: center;
overflow-y: auto;
}
<div id="calendar"></div>
<script src="./index.js"></script>
I want to detect a collision between two divs and I've tried to do that using the offsetLeft and offsetTop of each one of the divs and by using getBoundingClientRect(). But none of these work.
Is there a way I can get the exact coordinates in which these two elements touch?
const bigDiv = document.querySelector(".big")
const blue = document.querySelector(".blue")
const startBtn = document.querySelector("button")
const red = document.querySelector(".red")
const bigDivDimensions = bigDiv.getBoundingClientRect();
let speed = 5;
//let counter = 0;
let bigDivLeft = bigDivDimensions.left;
startGame();
function random() {
return Math.floor(Math.random() * (bigDivDimensions.width - 40));
}
function startGame() {
//let randomSnake = Math.floor(Math.random()*(bigDivDimensions.width-40));
//let randomApple = Math.ceil(Math.random()*(bigDivDimensions.width -40));
blue.style.left = random() + "px"
blue.style.top = random() + "px"
red.style.left = random() + "px"
red.style.top = random() + "px"
}
function move(e) {
let blueX = blue.offsetLeft;
let blueY = blue.offsetTop;
let key = e.keyCode;
if (key !== 37 && key !== 38 && key !== 39 && key !== 40) {
return
} else if (key === 37 && blueX > 3) {
blueX -= speed;
} else if (key === 38 && blueY > 3) {
blueY -= speed;
} else if (key === 39 && blueX < (bigDivDimensions.width - 25)) {
blueX += speed;
} else if (key === 40 && blueY < (bigDivDimensions.height - 23)) {
blueY += speed;
}
blue.style.left = blueX + "px";
blue.style.top = blueY + "px";
colision(blueX, blueY)
}
function colision(blueX, blueY) {
/* let redX = red.offsetLeft;
let redY = red.offsetTop;
if(redY === blueY || redX == blueX){console.log("hit")} */
let redRect = red.getBoundingClientRect();
let blueRect = blue.getBoundingClientRect();
if ((redRect.top < blueRect.bottom) || (redRect.bottom < blueRect.top) || (redRect.right > blueRect.left) || (redRect.left < blueRect.right)) {
console.log("hit")
}
}
startBtn.addEventListener("click", startGame)
document.addEventListener("keydown", move)
.big {
width: 400px;
height: 400px;
outline: 1px solid red;
margin: 0 auto;
position: relative;
}
.blue {
width: 20px;
height: 20px;
background: blue;
position: absolute;
}
.red {
width: 20px;
height: 20px;
background: red;
position: absolute;
}
<button type="button">Start Game</button>
<div class="big">
<div class="blue"></div>
<div class="red"></div>
</div>
codepen
Two rectangles do collide when their
horizontal side and vertical side
overlap.
Consider the following answer: Checking for range overlap
// l1 (line 1) --> [x1, x2]
// l2 (line 2) --> [x1, x2]
function checkOverlap(l1, l2) {
return ((l1[1] - l2[0]) > 0 && (l2[1] - l1[0]) > 0) ? true : false;
}
Check collision with range overlap for the x-side and y-side:
// Rectangle a --> [[x1, x2], [y1, y2]]
// Rectangle b --> [[x1, x2], [y1, y2]]
function collide(a, b) {
return (checkOverlap(a[0], b[0]) && checkOverlap(a[1], b[1]));
}
Spoiler
SVG-Example
var svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
var width = 200;
var height = 200;
svg.setAttribute("width", width);
svg.setAttribute("height", height);
document.body.appendChild(svg);
function createRect(svg, width, height, x, y, style, id) {
var rect = document.createElementNS("http://www.w3.org/2000/svg", "rect");
rect.setAttribute("width", width);
rect.setAttribute("height", height);
rect.setAttribute("x", x);
rect.setAttribute("y", y);
rect.setAttribute("style", style);
rect.setAttribute("id", id);
svg.appendChild(rect);
}
function createSquare(svg, side, x, y, style, id) {
createRect(svg, side, side, x, y, style, id);
}
function getRandomNumber(a, b) {
return Math.round(Math.random() * (b - a)) + a;
}
function createRandomSquare(svg, xRange, yRange, side, style, id) {
var x = getRandomNumber(xRange[0], xRange[1]);
var y = getRandomNumber(yRange[0], yRange[1]);
createSquare(svg, side, x, y, style, id);
return [
[x, x + side],
[y, y + side]
];
}
function checkOverlap(l1, l2) {
return ((l1[1] - l2[0]) > 0 && (l2[1] - l1[0]) > 0) ? true : false;
}
// [[x1, x2], [y1, y2]]
function collide(a, b) {
return (checkOverlap(a[0], b[0]) && checkOverlap(a[1], b[1]));
}
function getCoordinates(arr) {
return {
"top-left": [arr[0][0], arr[1][0]],
"top-right": [arr[0][1], arr[1][0]],
"bottom-left": [arr[0][1], arr[1][1]],
"bottom-right": [arr[0][0], arr[1][1]]
}
}
function run() {
var bs = document.getElementById("blueSquare");
var rs = document.getElementById("redSquare");
var output = document.getElementById("output");
output.innerHTML = "";
if (bs !== null && rs !== null) {
var parent = bs.parentNode;
parent.removeChild(bs);
parent.removeChild(rs);
}
var side = 30;
var blueSquare = createRandomSquare(svg, [0, (width - side)], [0, (height - side)], side, "fill:blue", "blueSquare");
var redSquare = createRandomSquare(svg, [0, (width - side)], [0, (height - side)], side, "fill:red", "redSquare");
var output1 = "Collision? " + collide(blueSquare, redSquare);
var output2 = "Blue square: " +
JSON.stringify(getCoordinates(blueSquare));
var output3 = "Red square: " + JSON.stringify(getCoordinates(redSquare));
output.innerHTML = output1 + "<br>" + output2 + "<br>" + output3 + "<br>";
}
var button = document.createElement("button");
button.setAttribute("id", "button");
button.textContent = "Run";
button.addEventListener("click", run);
document.body.appendChild(button);
svg {
border: 1px solid;
}
<div id="output"></div>
There are some runner(animation-image) in my program which move from position x to y when clicked on start button, i want to add a (reverse)button on completion that when clicked on that the image moves from y to x.
Here is the link of my js-fiddle: https://jsfiddle.net/o6egL4qr/
I have added the reverse button but when clicked on that the image doesn't move at all.
class raceManager {
raceCount = 0;
races = [];
addRace() {
var mainContainer = document.getElementById('mainContainer');
mainContainer.appendChild(document.createElement('br'));
var race = new raceClass(this.raceCount);
this.races.push(race);
this.raceCount++;
}
}
class raceClass {
runners = [];
count;
runnerCount = 0;
raceDiv = document.createElement('div');
raceNum = document.createElement('div');
startRaceButton = document.createElement('input');
addRunnerButton = document.createElement('input');
revRaceButton = document.createElement('input');
tableDiv = document.createElement('div');
tableNum = document.createElement('div');
startInterval;
startTime;
revStartTime;
reverseInterval;
constructor(number) {
// store the race no.
this.count = number;
// delcare the race div id
this.raceNum.id = 'raceNum' + this.count;
// delcare the table div id
this.tableNum.id = 'tableNum' + this.count;
// Add raceDiv to the race
document.getElementById('races').appendChild(this.raceDiv);
// Add tableDiv to the race
document.getElementById('tables').appendChild(this.tableDiv);
this.applyDivProperty();
this.initializeButtons();
}
applyDivProperty() {
// apply properties to the tableNum
this.tableNum.style.display = "inline-block";
// apply properties to the raceDiv
this.raceDiv.id = "Race" + this.count;
document.getElementById(this.raceDiv.id).classList.add("raceDivClass");
this.raceDiv.appendChild(this.raceNum);
document.getElementById(this.raceNum.id).innerHTML = '<p>Race: ' + this.count + '</p>';
// append the add race button
this.raceDiv.appendChild(this.addRunnerButton);
// apply properties to the tableDiv
this.tableDiv.id = "Table" + this.count;
document.getElementById(this.tableDiv.id).classList.add("tableClass");
this.tableDiv.appendChild(this.tableNum);
document.getElementById(this.tableNum.id).innerHTML = '<p>Table: ' + this.count + '</p>';
}
initializeButtons() {
// initialize add runner button
this.addRunnerButton.type = 'Button';
this.addRunnerButton.value = 'Add Runner';
this.addRunnerButton.id = 'AddRunner' + this.count;
this.addRunnerButton.onclick = this.addRunner.bind(this);
// initialize start race buttton
this.startRaceButton.type = 'Button';
this.startRaceButton.value = 'Start Race';
this.startRaceButton.id = "startRaceButton" + this.count;
this.startRaceButton.onclick = this.startRace.bind(this);
// initialize reverse race buttton
this.revRaceButton.type = 'Button';
this.revRaceButton.value = 'Reverse Race';
this.revRaceButton.id = "revRaceButton" + this.count;
this.revRaceButton.onclick = this.revRace.bind(this);
}
addRunner() {
var track = new Runner(this); //Initialize the runner object
this.runners.push(track); //Store the runner object in runners array of Race class
if (this.runnerCount > 0) {
// append the start race button
this.raceDiv.appendChild(this.startRaceButton);
}
this.runnerCount++;
}
startRace() {
this.startTime = Date.now();
this.startInterval = setInterval(() => {
this.runners.forEach(function(element) {
element.animate();
});
document.getElementById(this.startRaceButton.id).disabled = "true";
document.getElementById(this.addRunnerButton.id).disabled = "true";
}, 50);
}
stop() {
clearInterval(this.startInterval);
// append the start race button
this.raceDiv.appendChild(this.revRaceButton);
}
revRace() {
this.revStartTime = Date.now();
this.reverseInterval = setInterval(() => {
this.runners.forEach(function(element) {
element.animateReverse();
});
document.getElementById(this.revRaceButton.id).disabled = "true";
}, 50);
}
stopRev() {
clearInterval(this.reverseInterval);
}
}
class Runner {
count = 0;
parent;
track;
sprite;
timeTaken;
trackWidth;
element;
speed;
table;
printCount = 1;
stepCount = 1;
trackNum;
tbl;
lastStep;
constructor(race) {
// initialize the divs
this.parent = race;
this.track = document.createElement('div');
this.sprite = document.createElement('div');
this.table = document.createElement('table');
// assigns #id to table and track corresponding with parent div.
this.table.id = race.tableNum.id + '_Table_' + this.parent.runnerCount;
this.track.id = race.raceNum.id + '_Track_' + this.parent.runnerCount;
this.createUI();
this.timeTaken = ((Math.random() * 5) + 3);
this.speed = this.trackWidth / (this.timeTaken * 1000);
console.log(this.trackWidth, this.timeTaken);
console.log(this.timeTaken * 100);
}
createUI() {
this.count = this.parent.runnerCount;
this.createTable();
this.createTrack();
this.createSprite();
}
createTable() {
var parentDiv1 = document.getElementById(this.parent.tableNum.id);
parentDiv1.appendChild(this.table);
this.table.setAttribute = "border"
this.table.border = "1";
document.getElementById(this.table.id).classList.add("tableClass");
this.tbl = document.getElementById(this.table.id);
this.addRow("Track " + (this.count + 1), "");
this.addRow("Time", "Distance");
}
addCell(tr, val) {
var td = document.createElement('td');
td.innerHTML = val;
tr.appendChild(td)
}
addRow(val_1, val_2) {
var tr = document.createElement('tr');
this.addCell(tr, val_1);
this.addCell(tr, val_2);
this.tbl.appendChild(tr)
}
createTrack() {
var parentDiv = document.getElementById(this.parent.raceNum.id);
parentDiv.appendChild(this.track);
this.track.appendChild(this.sprite);
document.getElementById(this.track.id).classList.add("trackClass");
this.trackWidth = this.track.getBoundingClientRect().width;
}
createSprite() {
this.sprite.id = this.track.id + "_Runner";
document.getElementById(this.sprite.id).classList.add("spriteClass");
this.element = document.getElementById(this.sprite.id);
}
animate() {
// declare time variables
var timeNow = Date.now();
var timespent = timeNow - this.parent.startTime;
var diff = Math.floor(this.timeTaken * 100);
// step is position of sprite.
var step = timespent * this.speed;
// Print table for all tracks with 10 laps.
if ((Math.round(timespent / 50) * 50) == (Math.round(((diff - 25) * this.printCount) / 50) * 50)) {
this.addRow(this.printCount + ": " + timespent, (Math.floor(step)));
this.printCount++;
}
// check condition to stop
if (step > this.trackWidth - 23) {
document.getElementById(this.parent.raceNum.id).innerHTML += 'Winner: Runner' + (this.count + 1);
this.parent.stop();
}
this.element.style.left = step + 'px';
// ------------sprite animation----------------
// start position for the image slicer
var position = (3 - (Math.floor(step / 6.5) % 4)) * 25;
// we use the ES6 template literal to insert the variable "position"
this.element.style.backgroundPosition = `${position}px 0px`;
}
animateReverse() {
// declare time variables
var timeNow = Date.now();
var timespent = timeNow - this.parent.revStartTime;
var diff = Math.floor(this.timeTaken * 100);
console.log(this.count + " position of step " + this.element.style.left);
while (this.stepCount < 2) {
this.lastStep = parseFloat(this.element.style.left);
this.stepCount++;
}
console.log(this.count + " this is lastStep " + this.lastStep);
// step is position of sprite.
var step = this.lastStep - (this.speed * timespent);
// Print table for all tracks with 10 laps.
if ((Math.round(timespent / 50) * 50) == (Math.round(((diff - 25) * this.printCount) / 50) * 50)) {
this.addRow(this.printCount + ": " + timespent, (Math.floor(step)));
this.printCount++;
}
// check condition to stop
if (step < 25) {
document.getElementById(this.parent.raceNum.id).innerHTML += 'Winner: Runner' + (this.count + 1);
this.parent.stopRev();
}
this.element.style.left = step + 'px';
// ------------sprite animation----------------
// start position for the image slicer
//var position = (3 - (Math.floor(step / 6.5) % 4)) * 25;
//this.element.style.backgroundPosition = position + 'px 0px';
}
}
manager = new raceManager();
#tableContainer {
float: left;
}
#addRaces {
text-align: center;
}
.raceDivClass {
margin: 1% auto;
width: 60%;
text-align: center;
border: 1px solid;
}
.tableClass {
text-align: center;
border: 1px solid;
margin: 5px;
float: left;
}
.trackClass {
background-color: black;
height: 30px;
width: 98%;
margin: 1%;
position: relative;
}
.spriteClass {
background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGgAAAAeCAYAAADAZ1t9AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAASwSURBVGhD7Zq/axRBFMfv9u4MxATUSgsVm3QWigYbexuxUkiaKIqNoIUiwRSJoJLCxpSCaJoEYimo/4AgQVRIZxdBEIREYwzc5X74vndvlrm5mdnZ3dm9oPnAsDubu52Z93373pvNFQsJOXO9NUKHsU4vZPH90+IXPt/FA2kEmqbDTKcX8pza7K5I/vAtEJghge7zeSrwlDYbtYlmfWuILxWC8uBmUNoz/784wY4WaPRq9SGJcY+7Mt7GEOzUkB0pkGHi4Ci1K53TEK8h7tTE+pPywL6b3JXJQqBcQ7arQwR87AE34ElPUsPE1ZapODsErFHnnD7AfVWb9oylFYjVFcKoQuQC5kD55hB3Qxrbf17RYbHT+/fpCXGSOEmEaYcevofwhkRxXM0/VCi8azXr3+q1Xw8+LRxZ4cte4PneoHaQ2iVck0gVThVbyOhSRM9YOoFMyR9eW6KmLkAGYW6Vmjx47AViUfSkPC5V9p7nS23q1Z9zH+b33+KuF9iAOocUa0lcKFjubQJ2h51D5zbmIAVM9gc1mzgAE0kdFlFaq+JkQYQBV+FYOYoDGy9Tw3dgQ7QxnUBQUHxAtJfUhqllDhbWam4f525mJDCgMynufZFa13d6BILHsOeEjS6PUjMNtsRHFXgExI2V0PN6egiEkTzFMdlJgM/3zMd1H2Tzhjlqa53TLhLFbsvep9Cob70uFsuffbxJoHWZcq1A5CCZyDUZ7gtxotKDCsafdRHIZSFe9j9wBl1xoIJSuxhUVpIK5eB0JiILHo29EouDtVmLBF4IKjIbWOQkfzYVruENn+ESXFe+upBJeDMQVfWiyXQ5fFQV57oQLyLJL0VlsAfi06yJyhMuIOci7Efdqy0ENzxxonVFI22IY0NDHN1mykaX+nHAmKbw1qhtLLVaze8U1o6Jv9OmdaEYlI1lsLQGGVGwmMKbKZ8KXHIQxnUJn062CgVSFmQTRjySpr8n2nlb3lxTztl8W6+u3x0YOlylrpij1Vi0Hl3uxNx/U9MWIYSPtwZxclukSG2B4qreOTV+3skzBBgbuafVrJ0sVYbO8eUe4r5FMAgEbEnbSSC2l/p0grgRB1jHDGKqjt019kkwvoid4okS4D7O+Qji4MmxiQMonI2cGP/qYwMbt6LSAXFEzpCbyYaJcxuKBAwWJQ5EwATCTScLBeUhVGKRTIWBCgQsVYavcdcF8UZEnVveYPwXfIwNBMJCdF/GNeEZCFnahMzX1A0dgEi6MJALigP1SyiMCdu9wZH7sZBzkGpM5zcBljAZGdNPX964UAhKt0vlwbN8SQs2p/Xq2lTSfzU4hvK0OUily4b0PV1etI4Z+SbBFYMBrIPjO1QuT1N+GedLbVC1FYM9Hyk31fgScHYYE5JhD1Dz/r+fKPoqEJAMILAa1VRaU+HwaPnZwBR3vWJwJCDCUSonsKERKHJMrwLFAYbSbUwRyujanawMZfBikPXTEzvCgKhXPZmhe+/W2ZCuTWXpxQbgyWGFmhGILLb8p6V/AmnKa+Qd3783cCDz0JaGvgmEX4jyaRu8W6N8NM/dPGlvvvk8T5ye2r7mIIQ5PEl5/pyXc4FzIeOLZOMWCn8Bh1eBvOSZzIIAAAAASUVORK5CYII=');
position: absolute;
height: 30px;
width: 25px;
}
<!DOCTYPE html>
<html>
<head>
</head>
<body>
<div id="mainContainer">
<div id="addRaces">
<input type="Button" value="Add Race" onclick="manager.addRace()">
</div>
<div id="races">
</div>
<br>
</div>
<div id="tableContainer">
<div id="tables"></div>
</div>
</body>
</html>
I expect it to move from y to x after clicking the reverse button, but it is not moving.
When you run the reverse function the elements are no longer referencing the dom elements.
I am actually not sure why that is, maybe someone else can chime in.
Anyway, this will fix your problem:
Replace this.element.style.left = step + 'px';
With: document.getElementById(this.element.id).style.left = step + 'px';
SOLVED! see EDITs
I built a color picker app. Very simple; you click on the rgb color palette, and it creates a swatch with RGB values, HSL values, and HEX values.
I used this formula for the conversions.
Basically, I built my HSL values from the x and y mouse positions with a static 99% saturation.
From there, I created the color palette to pick from by converting HSL to RGB.
A click event on the palette will create RGB, HSL, and HEX swatches along with the values for each.
Since I can't get the RGB and HSL values to match I haven't incorporated the HEX values yet.
I finally found a working solution. Can someone tell me where my calculations drift away from the working solution? I don't just want to accept the working solution and move on; I want to know where my logic starts to break down.
Thanks so much for any help!
EDIT: So Bob__ suggested I use a better way to normalize my RGB values instead of just adding or subtracting 1 depending on their values. I added this function to make the RGB channels based on the hue value(-0.333 - 360.333)
function normalizeRGB(color){
var newVal = (360.333 - (-0.333))/(360.333 - (-0.333)) *
(color- (-0.333) + (-0.333));
return newVal;
}
index.html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Color Picker, Bro</title>
<link href='http://fonts.googleapis.com/css? family=Raleway:700,300' rel='stylesheet' type='text/css'>
<link rel="stylesheet" href="style.css">
<script src='https://code.jquery.com/jquery-2.1.3.min.js'></script>
<script type="text/javascript" src='myColorPickerSol.js'></script>
<!-- <script type="text/javascript" src='actualWorkingColorPickerSol.js'></script> -->
</head>
<body>
<div id='container'>
<h1>Color Picker, bro</h1>
<section id='canvas'></section>
<section id='readout'>
<p>HSL: <span id='hsl'></span></p>
<section id='swatchhsl'></section>
<p>RGB: <span id='rgb'></span></p>
<section id='swatchrgb'></section>
<p>HEX: <span id='hex'></span></p>
</section>
</section>
</div>
</body>
</html>
style.css:
body {
background: url(http://subtlepatterns.com/patterns/subtle_white_mini_waves.png);
}
#container {
margin: 0 auto;
width: 800px;
height: inherit;
text-align: center;
font-family: Raleway;
font-weight: 300;
}
#canvas {
margin: 0 auto;
border: 5px solid black;
box-sizing: border-box;
height: 360px;
width: 360px;
}
#readout {
background: rgba(117,117,117, .2);
margin: 20px auto;
height: 400px;
width: 360px;
border: 1px #333 solid;
box-sizing: border-box;
border-radius: 20px;
}
#swatchhsl,#swatchrgb {
margin: 0 auto;
height: 75px;
width: 95%;
border-radius: 20px;
}
p, span {
letter-spacing: 1px;
}
p {
font-weight: 700;
}
span {
font-weight: 300;
}
myColorPickerSol.js
$(document).ready(function(){
var canvas = $('#canvas');
//swatch matches closest when either pure blue, green or red; loses all accuracy when colors mix.
// dark blue gets really close. Purple gets really close, which makes me suspect the Green channel value is where the problem lies.
// y-axis as Luminace(0-100%)
// x-axis as Hue(0-360)
var yPos;
var lum;
var hue;// aka xPos;
var temp1;//for hslToRGB
var temp2;//for hslToRGB
var tempR;
var tempG;
var tempB;
var red;
var blue;
var green;
var realColVal;
$('#canvas').mousemove(function(event){
hue = Math.abs(event.offsetX);
hueForRGB = (hue/360);
yPos = Math.abs(event.offsetY);
lum = (yPos/360);
// console.log(lum + ' lum');
$(canvas).css({'background-color':'hsl('+ event.offsetX + ',99%,'+ Math.round(lum *100) + '%)'});
});
// swatch listener
$(canvas).click(function(event){
hsl2RGB(lum);
$('#rgb').text(red + ','+ green + ',' + blue);
$('#hsl').text(hue + ',99%,' + Math.round(lum * 100) + '%');
$(canvas).css({'background-color':'rgb('+ red + ','+ green + ','+ blue + ')'});
});
//red channel must be in upper third; green in middle third; blue in lower third.
function hsl2RGB(lum){
tempR = (hueForRGB + 0.333);
tempG = hueForRGB;
tempB = (hueForRGB - 0.333);
// set temporary lum based on whether it is above/below 50%
temp1 = lumMorOrLess50(lum);
// set secondary temporary lum value
temp2 = ((2.0 * (lum)) - temp1);
//-----------EDIT -----------------------------
// used the formula to make the tempR|G|B values between 0 and 1
// tempR = makeRGB01(tempR);
// tempG = makeRGB01(tempG);
// tempB = makeRGB01(tempB);
//-----------------------------------------------
red = Math.round(convert2RGB(tempR,temp1,temp2));
green = Math.round(convert2RGB(tempG,temp1,temp2));
blue = Math.round(convert2RGB(tempB,temp1,temp2));
//swatch appears on click for hsl and rgb
$('#swatchhsl').css({'background-color':'hsl('+ hue + ',99%,'+ Math.round(lum * 100 )+ '%)'});
$('#swatchrgb').css({'background-color':'rgb('+ red + ','+ green + ','+ blue + ')'});
};
//force tempR|G|B to be between 0-1
function makeRGB01(input) {
if(input > 1){
input -= 1.0;
} else if(input < 0){
input += 1.0;
};
return input;
};
//get value for each rgb channel
function convert2RGB(tempColVal, val1, val2){
//first convert tempColVal to between 0 and 1 then make it an RGB value
tempColVal = makeRGB01(tempColVal);
//next run 3 test;
if(6.0 * tempColVal < 1){
realColVal = (val2 + (val1 - val2) * 6 * tempColVal);
console.log(realColVal + 'test 1; val1: '+ val1 + 'val2: ' + val2 );
//-------EDIT ------------------------------------------
// test2 will set realColVal to val1 instead of tempColVal
//-------------------------------------------------------
} else if(2.0 * tempColVal < 1){
realColVal = val1;
console.log(realColVal + 'test 2');
} else if(3.0 * tempColVal < 2){
realColVal = (val2 + (val1 - val2)*(0.666 - tempColVal) * 6.0);
console.log(realColVal + 'test 3');
} else {
realColVal = val2;
console.log(realColVal + 'realColVal = default (temp 2)');
};
//-------EDIT ------------------------------------------
// normalize value before multiplying by 255
realColVal = normalizeRGB(realColVal);
//-------------------------------------------------------
// force value between 0 and 1 then set it to RGB scale and
// return
return (Math.abs(realColVal) * 255.0));
};
//configure temporary luminance value, temp1, based on luminance
function lumMorOrLess50(val){
if(val < 0.50){
return ((1.0 + 0.99) * val);
} else {
return ((.99 + val) - (val * .99));
};
};
});
This is the working solution, actualColorPickerSol.js What did I do differently?
$(function() {
console.log('Loaded, bro');
colorPicker();
});
function colorPicker() {
var canvas = $('#canvas');
canvas.on('mousemove', changeCanvasBackground);
canvas.on('click', printColorReadout);
}
function changeCanvasBackground(event) {
var xCoord = Math.abs(event.offsetX);
var yCoord = Math.abs(event.offsetY);
var rgbValues = 'rgb(' + rgb(hsl(xCoord, yCoord)) + ')';
$(this).css('background', rgbValues);
}
function printColorReadout(event) {
var xCoord = event.offsetX;
var yCoord = event.offsetY;
var hslValues = hsl(xCoord, yCoord);
var rgbValues = rgb(hslValues);
var hexValues = hex(rgbValues);
var hslString = parseHSL(hslValues);
var rgbString = parseRGB(rgbValues);
var hexString = parseHEX(hexValues);
$('#hsl').text(hslString);
$('#rgb').text(rgbString);
$('#hex').text(hexString);
$('#swatchhsl').css('background', hslString);
$('#swatchrgb').css('background', rgbString);
}
function hsl(xCoord, yCoord) {
// HSL = hsl(hue, saturation, luminance)
var hsl;
var hue = xCoord;
var luminance = Math.round(((yCoord / 360) * 100));
return [hue, 100, luminance];
}
function rgb(hslValues) {
var hue = hslValues[0];
var sat = hslValues[1] / 100;
var lum = hslValues[2] / 100;
var tempLum1, tempLum2, tempHue, tempR, tempG, tempB;
if (lum < .50) {
tempLum1 = lum * (1 + sat);
} else {
tempLum1 = (lum + sat) - (lum * sat);
}
tempLum2 = (2 * lum) - tempLum1;
tempHue = hue / 360;
tempR = tempHue + .333;
tempG = tempHue;
tempB = tempHue - .333;
//This is the only part I think I did differently.
//The code below makes sure the green and blue values
//are between 0 and 1, then it checks all the colors to
//make sure they are between 0 and 1. I tried this,
// and there was no change in the effect;
// the hsl and rgb values were still different.
if (tempG < 0) { tempG += 1};
if (tempG > 1) { tempG -= 1};
if (tempB < 0) { tempB += 1};
if (tempB > 1) { tempB -= 1};
var normalizedRGB = [tempR, tempG, tempB].map(function(color, idx) {
if (color < 0) { return color += 1};
if (color > 1) { return color -= 1};
return color;
});
var rgbArray = normalizedRGB.map(function(color) {
if (colorCondition1(color)) {
return tempLum2 + ( tempLum1 - tempLum2 ) * 6 * color;
} else if (colorCondition2(color)) {
return tempLum1;
} else if (colorCondition3(color)) {
return tempLum2 + (tempLum1 - tempLum2) * (.666 - color) * 6;
} else {
return tempLum2;
}
});
var rgbValues = rgbArray.map(function(color, idx) {
var convertedVal = color * 255;
return Math.round(convertedVal);
});
return rgbValues;
}
function hex(rgbValues) {
var r = rgbValues[0];
var g = rgbValues[1];
var b = rgbValues[2];
return [numToHex(r), numToHex(g), numToHex(b)];
}
function numToHex(num) {
var hexCode = num.toString(16);
if (hexCode.length < 2) { hexCode = "0" + hexCode; }
return hexCode;
}
function colorCondition1(val) {
return 6 * val < 1;
}
function colorCondition2(val) {
return 2 * val < 1;
}
function colorCondition3(val) {
return 3 * val < 2;
}
function parseHSL(hslValues) {
return [
"hsl(",
hslValues[0], ", ",
hslValues[1], "%, ",
hslValues[2], "%)"
].join('');
}
function parseRGB(rgbValues) {
return "rgb(" + rgbValues.join(', ') + ")";
}
function parseHEX(hexValues) {
return "#" + hexValues.join('');
}
Comparing your functions to the algorythm presented in the link you posted, I think that a more correct implementation of convert2RGB, as I told in my comments, may be:
function convert2RGB(tempColVal, val1, val2){
//first convert tempColVal to between 0 and 1 then make it an RGB value
tempColVal = makeRGB01(tempColVal);
//next run 3 test;
if(6.0 * tempColVal < 1){
realColVal = (val2 + (val1 - val2) * 6.0 * tempColVal);
// console.log(realColVal + 'test 1; val1: '+ val1 + 'val2: ' + val2 );
} else if(2.0 * tempColVal < 1){
realColVal = val1;
// console.log(realColVal + 'test 2');
} else if(3.0 * tempColVal < 2){
realColVal = (val2 + (val1 - val2)*(0.666 - tempColVal) * 6.0);
// console.log(realColVal + 'test 3');
} else {
realColVal = val2;
// console.log(realColVal + 'realColVal = default (temp 2)');
};
// Convert them to 8-bit by multiply them with 255 and return
return Math.round(realColVal * 255.0);
};
Edit:
Don't change your makeRGB01(), in that algorythm is used in the same way you did before calling convert2RGB(). Your mistake is applying that to returned corrected value too.