I'm using a colorwheel picker from this website, and I came up with a problem. When I add text above the color wheel, then scroll down and change the colorwheels color, the colorwheels cursor isn't contained in the actual circle. (It makes an imaginary circle in another place, and the colorwheels cursor is constrained in that imaginary circle.)
Another issue, which might be related to the first one is that if you zoom up in the browser, (in chrome: ctrl +) the colorwheels cursor stays position like a fixed element, it follows the scrolling.
How can I get the colorwheels cursor to always stay in the circle?
Relevant code (Line 210 - 211 in JSFiddle):
hsv_mapCursor.style.left = ((x * r + colorDiscRadius + doc.body.scrollLeft + doc.documentElement.scrollLeft) - 1) + 'px';
hsv_mapCursor.style.top = ((y * r + colorDiscRadius + doc.body.scrollTop + doc.documentElement.scrollTop) - 1) + 'px';
JSFiddle
var Tools = {}, // provides functions like addEvent, ... getOrigin, etc.
startPoint,
currentTarget,
currentTargetHeight = 0;
/* ----------------------------------- */
/* --------- Tool Functions ---------- */
/* ----------------------------------- */
function getOrigin(elm) {
var box = (elm.getBoundingClientRect) ? elm.getBoundingClientRect() : {
top: 0,
left: 0
},
doc = elm && elm.ownerDocument,
body = doc.body,
win = doc.defaultView || doc.parentWindow || window,
docElem = doc.documentElement || body.parentNode,
clientTop = docElem.clientTop || body.clientTop || 0, // border on html or body or both
clientLeft = docElem.clientLeft || body.clientLeft || 0;
return {
left: box.left + (win.pageXOffset || docElem.scrollLeft) - clientLeft,
top: box.top + (win.pageYOffset || docElem.scrollTop) - clientTop
};
}
function addEvent(obj, type, func) {
addEvent.cache = addEvent.cache || {
_get: function(obj, type, func, checkOnly) {
var cache = addEvent.cache[type] || [];
for (var n = cache.length; n--;) {
if (obj === cache[n].obj && '' + func === '' + cache[n].func) {
func = cache[n].func;
if (!checkOnly) {
cache[n] = cache[n].obj = cache[n].func = null;
cache.splice(n, 1);
}
return func;
}
}
},
_set: function(obj, type, func) {
var cache = addEvent.cache[type] = addEvent.cache[type] || [];
if (addEvent.cache._get(obj, type, func, true)) {
return true;
} else {
cache.push({
func: func,
obj: obj
});
}
}
};
if (!func.name && addEvent.cache._set(obj, type, func) || typeof func !== 'function') {
return;
}
if (obj.addEventListener) obj.addEventListener(type, func, false);
else obj.attachEvent('on' + type, func);
}
function removeEvent(obj, type, func) {
if (typeof func !== 'function') return;
if (!func.name) {
func = addEvent.cache._get(obj, type, func) || func;
}
if (obj.removeEventListener) obj.removeEventListener(type, func, false);
else obj.detachEvent('on' + type, func);
}
Tools.getOrigin = getOrigin;
Tools.addEvent = addEvent;
Tools.removeEvent = removeEvent;
/* ---------------------------------- */
/* ---- HSV-circle color picker ----- */
/* ---------------------------------- */
// Create HSV-circle Relavent Variables
var hsv_map = document.getElementById('hsv_map'),
colorDiskWrapper = document.getElementById('colorDiskWrapper'),
colorDisc = document.getElementById('surface'),
hsv_mapCover = document.getElementById('cover'),
hsv_mapCursor = document.getElementById('hsv-cursor'),
luminenceBarWrapper = document.getElementById('luminenceBarWrapper'),
hsv_barBGLayer = document.getElementById('bar-bg'),
hsv_barWhiteLayer = document.getElementById('bar-white'),
luminanceBar = document.getElementById('luminanceBar'),
hsv_barcursor = document.getElementById('hsv-barcursor'),
hsv_barCursors = document.getElementById('hsv-barcursors'),
hsv_barCursorsCln = hsv_barCursors.className,
luminanceBarContext = luminanceBar.getContext("2d"),
colorDiscHeight = colorDisc.offsetHeight,
colorDiscRadius = colorDisc.offsetHeight / 2;
// generic function for drawing a canvas disc
var drawDisk = function(ctx, coords, radius, steps, colorCallback) {
var x = coords[0] || coords, // coordinate on x-axis
y = coords[1] || coords, // coordinate on y-axis
a = radius[0] || radius, // radius on x-axis
b = radius[1] || radius, // radius on y-axis
angle = 360,
rotate = 0,
coef = Math.PI / 180;
ctx.save();
ctx.translate(x - a, y - b);
ctx.scale(a, b);
steps = (angle / steps) || 360;
for (; angle > 0; angle -= steps) {
ctx.beginPath();
if (steps !== 360) ctx.moveTo(1, 1); // stroke
ctx.arc(1, 1, 1, (angle - (steps / 2) - 1) * coef, (angle + (steps / 2) + 1) * coef);
if (colorCallback) {
colorCallback(ctx, angle);
} else {
ctx.fillStyle = 'black';
ctx.fill();
}
}
ctx.restore();
},
drawCircle = function(ctx, coords, radius, color, width) { // uses drawDisk
width = width || 1;
radius = [
(radius[0] || radius) - width / 2, (radius[1] || radius) - width / 2
];
drawDisk(ctx, coords, radius, 1, function(ctx, angle) {
ctx.restore();
ctx.lineWidth = width;
ctx.strokeStyle = color || '#000';
ctx.stroke();
});
};
if (colorDisc.getContext) {
drawDisk( // HSV color wheel with white center
colorDisc.getContext("2d"), [colorDisc.width / 2, colorDisc.height / 2], [colorDisc.width / 2 - 1, colorDisc.height / 2 - 1],
360,
function(ctx, angle) {
var gradient = ctx.createRadialGradient(1, 1, 1, 1, 1, 0);
gradient.addColorStop(0, 'hsl(' + (360 - angle + 0) + ', 100%, 50%)');
gradient.addColorStop(1, "#FFFFFF");
ctx.fillStyle = gradient;
ctx.fill();
}
);
drawCircle( // gray border
colorDisc.getContext("2d"), [colorDisc.width / 2, colorDisc.height / 2], [colorDisc.width / 2, colorDisc.height / 2],
'#555',
3
);
}
// draw the luminanceBar bar
var gradient = luminanceBarContext.createLinearGradient(0, 0, 0, colorDiscHeight);
gradient.addColorStop(0, "transparent");
gradient.addColorStop(1, "black");
luminanceBarContext.fillStyle = gradient;
luminanceBarContext.fillRect(0, 0, luminanceBar.offsetWidth, colorDiscHeight);
// Manage Color Wheel Mouse Events
var renderHSVPicker = function(color) { // used in renderCallback of 'new ColorPicker'
var pi2 = Math.PI * 2,
x = Math.cos(pi2 - color.hsv.h * pi2),
y = Math.sin(pi2 - color.hsv.h * pi2),
r = color.hsv.s * colorDiscRadius;
hsv_mapCover.style.opacity = 1 - color.hsv.v;
// this is the faster version...
hsv_barWhiteLayer.style.opacity = 1 - color.hsv.s;
hsv_barBGLayer.style.backgroundColor = 'rgb(' +
color.hueRGB.r + ',' +
color.hueRGB.g + ',' +
color.hueRGB.b + ')';
var doc = window.document;
hsv_mapCursor.style.left = ((x * r + colorDiscRadius + doc.body.scrollLeft + doc.documentElement.scrollLeft) - 1) + 'px';
hsv_mapCursor.style.top = ((y * r + colorDiscRadius + doc.body.scrollTop + doc.documentElement.scrollTop) - 1) + 'px';
hsv_mapCursor.style.borderColor = hsv_barcursor.style.borderColor = color.RGBLuminance > 0.22 ? 'black' : 'white';
hsv_barcursor.style.top = (((1 - color.hsv.v) * colorDiscRadius * 2 + doc.body.scrollTop + doc.documentElement.scrollTop) + -7) + 'px';
};
var myColor = new Colors(),
doRender = function(color) {
renderHSVPicker(color);
},
renderTimer,
// those functions are in case there is no ColorPicker but only Colors involved
startRender = function(oneTime) {
if (oneTime) { // only Colors is instanciated
doRender(myColor.colors);
} else {
renderTimer = window.setInterval(
function() {
doRender(myColor.colors);
// http://stackoverflow.com/questions/2940054/
}, 13); // 1000 / 60); // ~16.666 -> 60Hz or 60fps
}
},
stopRender = function() {
window.clearInterval(renderTimer);
};
// Create Event Functions
var hsvDown = function(e) { // mouseDown callback
var target = e.target || e.srcElement;
if (e.preventDefault) e.preventDefault();
//currentTarget = target.id ? target : target.parentNode;
if (target === hsv_mapCover || target === hsv_mapCursor || target === hsv_barcursor) {
currentTarget = target.parentNode;
} else if (target === hsv_barCursors) {
currentTarget = target;
} else {
return;
}
startPoint = Tools.getOrigin(currentTarget);
currentTargetHeight = currentTarget.offsetHeight; // as diameter of circle
Tools.addEvent(window, 'mousemove', hsvMove);
hsv_mapCover.style.cursor = hsv_mapCursor.style.cursor = 'none';
hsvMove(e);
startRender();
},
hsvMove = function(e) { // mouseMove callback
var r, x, y, h, s;
if (currentTarget === colorDiskWrapper) { // the circle
r = currentTargetHeight / 2;
x = e.clientX - startPoint.left - r;
y = e.clientY - startPoint.top - r;
h = 360 - ((Math.atan2(y, x) * 180 / Math.PI) + (y < 0 ? 360 : 0));
s = (Math.sqrt((x * x) + (y * y)) / r) * 100;
myColor.setColor({
h: h,
s: s
}, 'hsv');
} else if (currentTarget === hsv_barCursors) { // the luminanceBar
myColor.setColor({
v: (currentTargetHeight - (e.clientY - startPoint.top)) / currentTargetHeight * 100
}, 'hsv');
}
};
// Initial Rendering
doRender(myColor.colors);
// Adde Events To Objects
Tools.addEvent(hsv_map, 'mousedown', hsvDown); // event delegation
Tools.addEvent(window, 'mouseup', function() {
Tools.removeEvent(window, 'mousemove', hsvMove);
hsv_mapCover.style.cursor = hsv_mapCursor.style.cursor = hsv_barCursors.style.cursor = 'pointer';
stopRender();
});
#wrapper {
width: 500px;
height: 500px;
background-color: pink;
}
#hsv_map {
width: 540px;
height: 500px;
position: absolute;
}
#colorDiskWrapper {
border-radius: 50%;
position: relative;
width: 500px;
}
#cover {
background-color: black;
border-radius: 50%;
cursor: pointer;
width: 100%;
height: 100%;
position: absolute;
top: 0;
left: 0;
}
#bar-bg,
#bar-white {
position: absolute;
right: 0;
top: 0;
width: 15px;
height: 500px;
}
#bar-white {
background-color: #fff;
}
#hsv-cursor {
position: absolute;
border: 2px solid #eee;
border-radius: 50%;
width: 9px;
height: 9px;
margin: -5px;
cursor: pointer;
}
.no-cursor #hsv-cursor,
#hsv_map.no-cursor #cover {
cursor: none;
/* also works in IE8 */
/*url(_blank.gif), url(_blank.png), url(_blank.cur), auto;*/
}
#luminanceBar {
position: absolute;
right: 0;
top: 0;
}
#hsv-barcursors {
position: absolute;
right: -7.5px;
width: 30px;
top: 0;
height: 500px;
overflow: hidden;
cursor: pointer;
}
#hsv-barcursor {
position: absolute;
right: 7.5px;
width: 11px;
height: 11px;
border-radius: 50%;
border: 2px solid black;
margin-bottom: 5px;
}
<script src="https://rawgit.com/PitPik/colorPicker/master/colors.js"></script>
Lorem ipsum dolor sit amet, his id nonumy quaestio efficiantur. Quidam deleniti eum no, pri adhuc putant ea. Nec cu melius accusata, et vim lorem quaeque minimum. At aperiri principes complectitur eam, quo ne soleat accusamus ullamcorper, sed fugit adipiscing
no. Eripuit consequuntur quo no, his accusamus persequeris ea. Exerci delenit ad eos, ei sea tamquam ancillae necessitatibus. Est legere possim mnesarchum an. Vim nonumes expetendis ea, mei te mutat propriae platonem, per ut feugiat reprimique. Has deserunt
nominati liberavisse ut. Per cibo ubique omittam in, has te decore denique, justo nominavi id pro. Ei ius paulo deserunt pertinacia. Nec nostro expetenda id. Nec no esse interesset, id qui ferri errem. Qui possim ponderum platonem ea, nostro epicuri nam
in. Ei consul ubique conclusionemque eum, et alterum apeirian nam. Quem odio adipiscing has ei. Et has solum perfecto tincidunt, quo no indoctum prodesset. Pri ut tritani ceteros tractatos, in legere disputationi mea. Ne mel mutat graeci, mei eu doctus
appetere. Ius dicam imperdiet id, duo commune phaedrum ut, ut reque minim vis. Instructior signiferumque mea id. Nam ut vocibus recusabo consulatu. No mea postea mandamus prodesset, his hinc iusto in, no atqui consequat his. Discere sapientem id vel,
eam ea idque lucilius, ne eos labitur pericula. Ea dicat sonet nec, te pri libris putant eirmod, ancillae voluptatibus ut eam. Dicat commune necessitatibus nec cu, an enim purto menandri vel, eu ferri altera admodum duo.Lorem ipsum dolor sit amet, his
id nonumy quaestio efficiantur. Quidam deleniti eum no, pri adhuc putant ea. Nec cu melius accusata, et vim lorem quaeque minimum. At aperiri principes complectitur eam, quo ne soleat accusamus ullamcorper, sed fugit adipiscing no. Eripuit consequuntur
quo no, his accusamus persequeris ea. Exerci delenit ad eos, ei sea tamquam ancillae necessitatibus. Est legere possim mnesarchum an. Vim nonumes expetendis ea, mei te mutat propriae platonem, per ut feugiat reprimique. Has deserunt nominati liberavisse
ut. Per cibo ubique omittam in, has te decore denique, justo nominavi id pro. Ei ius paulo deserunt pertinacia. Nec nostro expetenda id. Nec no esse interesset, id qui ferri errem. Qui possim ponderum platonem ea, nostro epicuri nam in. Ei consul ubique
conclusionemque eum, et alterum apeirian nam. Quem odio adipiscing has ei. Et has solum perfecto tincidunt, quo no indoctum prodesset. Pri ut tritani ceteros tractatos, in legere disputationi mea. Ne mel mutat graeci, mei eu doctus appetere. Ius dicam
imperdiet id, duo commune phaedrum ut, ut reque minim vis. Instructior signiferumque mea id. Nam ut vocibus recusabo consulatu. No mea postea mandamus prodesset, his hinc iusto in, no atqui consequat his. Discere sapientem id vel, eam ea idque lucilius,
ne eos labitur pericula. Ea dicat sonet nec, te pri libris putant eirmod, ancillae voluptatibus ut eam. Dicat commune necessitatibus nec cu, an enim purto menandri vel, eu ferri altera admodum duo.
<div id="wrapper">
<div id="hsv_map">
<div id="colorDiskWrapper">
<canvas id="surface" width="500" height="500"></canvas>
<div id="cover"></div>
<div id="hsv-cursor"></div>
</div>
<div id="luminenceBarWrapper">
<div id="bar-bg"></div>
<div id="bar-white"></div>
<canvas id="luminanceBar" width="15" height="500"></canvas>
<div id="hsv-barcursors" id="hsv_cursors">
<div id="hsv-barcursor"></div>
</div>
</div>
</div>
</div>
add this to your css :
html, body {
width: 100%;
height: 100%;
overflow-x: hidden;
position: relative;
}
JSFIDDLE
Related
The problem I encounter is that I dont know how to overlay a text, image, and video on a javascript. I tried to find it on youtube and google but I find no answer. Can somebody help me? Thanks!
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="./styles.css">
<title></title>
</head>
<body style="background-color: black">
<canvas id="Matrix" style="width: 100%; height: 100%;"></canvas>
<script src="./index.js"></script>
</body>
</html>
html {
background: black;
height: 100%;
overflow: hidden;
}
body {
margin: 0;
padding: 0;
height: 100%;
}
const canvas = document.getElementById('Matrix');
const context = canvas.getContext('2d');
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
const katakana = 'アァカサタナハマヤャラワガザダバパイィキシチニヒミリヰギジヂビピウゥクスツヌフムユュルグズブヅプエェケセテネヘメレヱゲゼデベペオォコソトノホモヨョロヲゴゾドボポヴッン';
const latin = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
const nums = '0123456789';
const alphabet = katakana + latin + nums;
const fontSize = 16;
const columns = canvas.width/fontSize;
const rainDrops = [];
for( let x = 0; x < columns; x++ ) {
rainDrops[x] = 1;
}
const draw = () => {
context.fillStyle = 'rgba(0, 0, 0, 0.05)';
context.fillRect(0, 0, canvas.width, canvas.height);
context.fillStyle = '#0F0';
context.font = fontSize + 'px monospace';
for(let i = 0; i < rainDrops.length; i++)
{
const text = alphabet.charAt(Math.floor(Math.random() * alphabet.length));
context.fillText(text, i*fontSize, rainDrops[i]*fontSize);
if(rainDrops[i]*fontSize > canvas.height && Math.random() > 0.975){
rainDrops[i] = 0;
}
rainDrops[i]++;
}
};
setInterval(draw, 30);
I am expecting the javascript to be the background of my entire website while only using HTML,CSS, and JS.
Using this answer solution, you can use the properties position: absolute to place elements above the canvas.
const canvas = document.getElementById('Matrix');
const context = canvas.getContext('2d');
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
const katakana = 'アァカサタナハマヤャラワガザダバパイィキシチニヒミリヰギジヂビピウゥクスツヌフムユュルグズブヅプエェケセテネヘメレヱゲゼデベペオォコソトノホモヨョロヲゴゾドボポヴッン';
const latin = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
const nums = '0123456789';
const alphabet = katakana + latin + nums;
const fontSize = 16;
const columns = canvas.width / fontSize;
const rainDrops = [];
for (let x = 0; x < columns; x++) {
rainDrops[x] = 1;
}
const draw = () => {
context.fillStyle = 'rgba(0, 0, 0, 0.05)';
context.fillRect(0, 0, canvas.width, canvas.height);
context.fillStyle = '#0F0';
context.font = fontSize + 'px monospace';
for (let i = 0; i < rainDrops.length; i++) {
const text = alphabet.charAt(Math.floor(Math.random() * alphabet.length));
context.fillText(text, i * fontSize, rainDrops[i] * fontSize);
if (rainDrops[i] * fontSize > canvas.height && Math.random() > 0.975) {
rainDrops[i] = 0;
}
rainDrops[i]++;
}
};
setInterval(draw, 30);
html {
background: black;
height: 100%;
overflow: hidden;
}
body {
margin: 0;
padding: 0;
height: 100%;
}
/* Color of the text - you can change it as you need. */
div {
color: red;
}
/* These are the properties applied to div that has the "innerDiv" class - you can change it as you need. */
.innerDiv {
position: absolute;
left: 10px;
top: 10px;
}
<canvas id="Matrix" style="width: 100%; height: 100%;"></canvas>
<script src="./index.js"></script>
<div id="wrapper">
<div class="innerDiv">
<span>This is an title example:</span>
<br/>
<p>
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor
in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
</p>
</div>
</div>
I've been trying to have multiple paragraphs with different width but each time a line breaks, the line after goes in a new paragraph <p></p>. I need four of them that I can later style independently.
Here is a screenshot of what I'm trying to achieve, as an exemple:
here is the original question where I found the original script.
What I would like to do:
I've managed to adapt it and understand it quite a bit, to make it work when the lines are all the same. Basically :
<p style="width:700px">I am some ra</p>
<p style="width:700px">ndom text. I</p>
<p style="width:700px">am Some text</p>
<p style="width:700px">.blabla</p>
But now I'm facing an issue:
I'd like to specify different width for the individual lines in my css so the script breaks them perfectly. In other words: A line of text takes all the width of the div paragraph and the remaining text moves into another paragraph (for exemple #text2) until we reach four div paragraphs.
Something like that:
<p style="width:50px">I am some ra</p>
<p style="width:900px">ndom text. I</p>
<p style="width:800px">am Some text</p>
<p style="width:1000px">.blabla</p>
EDIT:
There is some progress so far. The text seems to breaks related to the width of the paragraph just before.
Here is the project:
var p = document.getElementById("p");
var menu = document.getElementsByClassName("menu");
var width = p.clientWidth;
var parent = p.parentNode;
var line = document.createElement("span");
line.style.display = "inline-block";
line.style.visibility = "hidden";
var content = p.innerHTML;
document.body.appendChild(line);
var start = 0;
var i = 1;
let run = 1;
while (i < content.length) {
line.innerHTML = content.substring(start, i);
console.log(i + " " + content.length);
console.log("#text" + run + " width: " + menu[run].clientWidth);
console.log("run: " + run);
if (line.clientWidth > menu[run + 1].clientWidth) {
var newp = document.createElement("p");
newp.id = "text" + run;
newp.className = "menu";
newp.innerHTML = content.substring(start, i - 1);
parent.insertBefore(newp, p);
start = i - 1;
i = start + 1;
run++;
} else {
i++;
}
}
newp = document.createElement("p");
newp.id = "textbis" + run;
newp.className = "menu";
newp.innerHTML = content.substring(start);
parent.insertBefore(newp, p);
parent.removeChild(p);
div {
word-break: break-all;
background-color: lightblue;
width: 700px;
}
p {
background-color: lightgreen;
}
#text0 {
width: 50px;
}
#text1 {
width: 50px;
}
#text2 {
width: 200px;
}
#text3 {
width: 500px;
}
#text4 {
width: 700px;
}
<div>
<p class="menu" id="text1"></p>
<p class="menu" id="text2"></p>
<p class="menu" id="text2"></p>
<p class="menu" id="p">
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Saepe,
aliquam? Lorem ipsum dolor sit amet consectetur adipisicing elit.
Quibusdam quaerat iste earum quos eligendi atque aliquam veniam facilis
quis ducimus. Lorem ipsum dolor sit amet consectetur adipisicing elit.
Iure, quisquam! Temporibus consequuntur laboriosam quos odio maxime
iusto at dolore quod ipsa eaque voluptas mollitia, vel odit inventore
sapiente aut!
</p>
</div>
It might not be the best choice performance-wise, but here's one way to do it.
let textarea = document.querySelector("textarea");
let output = document.querySelector("textarea + div");
let widths = [100, 450, 400, 500, 9999];
textarea.addEventListener("input", () => {
output.innerHTML = null;
console.time("split took");
let lines = splitTextByLineWidths(textarea.value, widths);
console.timeEnd("split took");
lines.forEach((line, i) => {
let p = document.createElement("p");
if (widths[i] < window.innerWidth) {
p.style.width = widths[i] + "px";
}
p.textContent = line;
output.append(p);
});
});
textarea.dispatchEvent(new Event("input"));
function splitTextByLineWidths(text, lineWidths) {
let lines = [];
let renderer = document.createElement("div");
renderer.style.position = "absolute";
renderer.style.left = window.innerWidth * -3 + "px";
renderer.style.top = window.innerHeight * -3 + "px";
document.body.appendChild(renderer);
lineWidths.forEach(lineWidth => {
let measure = document.createElement("div");
measure.style.display = "table";
measure.textContent = "dummy";
renderer.appendChild(measure);
let lineHeight = measure.offsetHeight;
let activeText = text;
measure.textContent = activeText;
measure.style.width = lineWidth + "px";
let height = measure.offsetHeight;
while (height > lineHeight) {
activeText = activeText.slice(0, -1);
measure.textContent = activeText;
height = measure.offsetHeight;
}
lines.push(activeText);
text = text.slice(activeText.length);
});
renderer.remove();
return lines;
}
textarea + div {
background-color: lightblue;
padding: 1em;
}
textarea + div p {
background-color: peachpuff;
}
<textarea cols="100" rows="6">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque varius nisi purus, in dignissim justo egestas et. Suspendisse suscipit, metus vitae facilisis cursus, diam est malesuada tellus, eu viverra risus sapien nec neque.</textarea>
<div></div>
This method can be further improved by using smarter ways to trim the text. Currently, it's triming a single character and checking if the text overflows the first line. This is an expensive operation.
One can devise a way to increase the trim length, and instead of going one way (trim only), can go both ways (trim and grow/put back) when the text underflows the width.
I am building a real-time HTML highlighter so that when a user selects a range of text that text is surrounded with span elements that have a background property.
Here is the fiddle: https://jsfiddle.net/4hd2vrex/
Problem is that this can get quite messy when users do multiple selects, the spans get nested and I get content like this:
<span style="background-color: rgb(255, 255, 131);">
r
<span style="background-color: rgb(255, 255, 131);">
<span style="background-color: rgb(255, 255, 131);">
e
</span>
p
</span>
r
<span style="background-color: rgb(255, 255, 131);">
e
</span>
h
<span style="background-color: rgb(255, 255, 131);">
end
</span>
e
<span style="background-color: rgb(255, 255, 131);">
rit
</span>
</span>
Holy Jackpot Batman! To remedy this I have the following idea:
Before adding any spans just replace all the selected text, span tags and all, with the original selected text window.getSelection().
So for example, if I selected that mess of spans above, before wrapping my selected text with more spans I would replace those spans with window.getSelection() which is just the text reprehenderit and I would get.
<span style="background-color: rgb(255, 255, 131);">reprehenderit</span>
Q:
How do I replace my selection with the selected text?
I have done the whole highlight text with my ways,not use window.Selection API,but use:select(start,end).then(merge).then(filter).then(highlight).and the most interesting things is that it can be highlight complex element,not even if text only.I found that the select api can also write a wysiwyg html editor,so I shared it to everyone who interesting on selection problem,and wish to help you,good question!
(function (context, factory) {
if (typeof module != 'undefined' && typeof module.exports == 'object') {
module.exports = factory(context);
} else {
factory(context, true);
}
})(window || this, function (context, bind) {
function promise(executor) {
return new Promise(executor);
}
var $TYPE = 'nodeType', $TEXT = 'textContent', $PARENT = 'parentNode', $NEXT = 'nextSibling', $FIRST = 'firstChild', NIL = {};
function leaf(node) {
return node[$TYPE] == 3;
}
function next(node, tree) {
var it = tree ? node[$FIRST] || node[$NEXT] : node[$NEXT];
if (it) {
if (leaf(it)) return it;
return next(it, true);
}
var parent = node[$PARENT];
return parent && next(parent);
}
function parent(node) {
return node[$PARENT];
}
function wrap(node, start, end) {
if (!node) throw 'node is null';
if (!leaf(node)) throw 'node is not a leaf:' + node.tagName;
var rawText = node[$TEXT];
var rawLength = rawText.length;
var self = {
node: node,
text: function (text) {
if (text !== undefined) {
node.textContent = text;
return wrap(node, 0, text.length);
}
return rawText.substring(self.start(), self.end());
},
is: function (other) {
return node == other.node;
},
start: function () {
return start === NIL || !start ? 0 : start;
},
end: function () {
return end === NIL || !end ? rawLength : end;
},
length: function () {
return self.end() - self.start();
},
to: function (end) {
return wrap(node, self.start(), end.end());
},
toLast: function () {
return wrap(node, start, rawLength);
},
next: function () {
var it = next(node);
return it && wrap(it);
},
split: function () {
if (self.length() >= rawLength) return self;
var stack = [0].concat(self.start() || []).concat(self.end()).concat(self.end() != rawLength ? rawLength : []);
var start = stack.shift();
var separated = [];
while (stack.length) {
var end = stack.shift();
var text = document.createTextNode(rawText.substring(start, end));
self.after(text);
separated.push(wrap(text));
start = end;
}
self.remove();
return !self.start() ? separated[0] : separated[1];
},
remove: function (optimized) {
var parent = node[$PARENT];
if (optimized && parent.childNodes.length == 1) {
parent[$PARENT].removeChild(parent);
}
parent.removeChild(node);
return this;
},
merge: function (other) {
var it = self.split();
return it.text(other.split().remove(true).text() + it.text());
},
after: function (e) {
node[$PARENT].insertBefore(e, node);
return this;
},
wrap: function (e) {
e.appendChild(self.split().after(e).node);
}
};
return self;
}
function select(start, end) {
return promise(function (resolve) {
start = wrap(start.text, start.offset, NIL), end = wrap(end.text, NIL, end.offset);
var selected = [];
while (start) {
if (start.is(end)) {
selected.push(start.to(end));
break;
}
selected.push(start.toLast());
start = start.next();
}
resolve(selected);
});
}
function merge(filter) {
return function (parts) {
var result = [parts.shift()];
while (parts.length) {
var prev = result.pop();
var next = parts.shift();
if (filter(prev.node, next.node)) {
result.push(next.merge(prev));
} else {
result.push(prev);
result.push(next);
}
}
return result;
}
}
function filter(test) {
return function (parts) {
return parts.filter(function (part) {
return test(part.node);
});
}
}
function apply(consume) {
return function (parts) {
return parts.forEach(function (part) {
return consume(part);
});
}
}
var exports = {
__esModule: true,
default: select,
select: select,
merge: merge,
filter: filter,
apply: apply
};
if (bind)for (var name in exports)context[name] = exports[name];
return exports;
});
(function () {
var COMPONENT_ID = 'highlight-' + +new Date;
var highlighter = {
init: function () {
this.bindEvents();
},
/**
*
*/
bindEvents: function () {
var self = this;
$('.swatch').on('click', function () {
$('.swatch').removeClass('active');
$(this).addClass('active');
});
$('.content').mouseup(function () {
var current = self.actived();
if (current.hasClass('clear')) {
self.clear();
} else {
self.highlight();
}
});
},
actived: function () {
return $('.swatch.active');
},
color: function () {
return this.actived().css('background-color');
},
/**
*
*/
highlight: function () {
var self = this;
var selection = self.getSelection();
if (selection) {
self.select(selection.getRangeAt(0)).//
then(merge(function (left, right) {
var p1 = left.parentNode;
var p2 = right.parentNode;
var a1 = self.compare(left);
var a2 = self.compare(right);
return (a1 && a2 && p1.parentNode == p2.parentNode) ||
(!a1 && !a2 && p1 == p2) ||
(a1 && !a2 && p1.parentNode == p2) ||
(!a1 && a2 && p2.parentNode == p1);
})).then(filter(function (part) {
return !self.compare(part);
})).then(function (parts) {
parts.map(function (node) {
node.wrap(self.component());
});
}).catch(function (e) {
console.log(e);
});
selection.removeAllRanges();
}
},
component: function () {
return $('<span data-toggle="' + COMPONENT_ID + '">').css('background-color', this.color()).get(0);
},
compare: function (text) {
var self = this;
var parent = $(text).parent();
var highlighted = parent.is(self.selector());
var color = parent.css('background-color');
return highlighted && color == self.color();
},
selector: function () {
return '[data-toggle="?"]'.replace(/\?/, COMPONENT_ID);
},
clear: function () {
var self = this;
var selection = self.getSelection();
if (selection) {
self.select(selection.getRangeAt(0)).then(apply(function (part) {
var text = $(part.split().node);
while (true) {
var comp = text.closest(self.selector());
if (!comp || !comp.length) {
break;
}
var children = comp.contents();
var first = children[0], last = children[children.length - 1];
if (text.is(last)) {
comp.after(text);
} else if (text.is(first)) {
comp.before(text);
} else {
var heading = comp.clone().empty();
for (var i = 0; i < children.length; i++) {
if (text.is(children[i])) {
break;
}
heading.append(children[i]);
}
comp.before(heading).before(text);
}
if (first == last) comp.remove();
}
}));
selection.removeAllRanges();
}
},
select: function (range) {
return select(
{text: range.startContainer, offset: range.startOffset},
{text: range.endContainer, offset: range.endOffset}
);
},
getSelection: function () {
var sel = window.getSelection();
return /^\s*$/.test(self && sel.toString()) ? null : sel;
}
};
highlighter.init();
})();
body {
margin: 0;
background: #fafafa;
box-shadow: 0 0 5rem rgba(0, 0, 0, 0.25) inset;
}
::-moz-selection {
background-color: rgba(0, 0, 0, 0.2);
}
::selection {
background-color: rgba(0, 0, 0, 0.2);
}
.content {
padding: 100px;
}
.footer {
padding: 0 100px 0 100px;
flex-basis: 100%;
height: 60px;
background: #292B2C;
position:fixed;top:0;width:100%;
}
.footer .items-left {
float: left;
}
.footer-item {
line-height: 60px;
}
#colors {
padding: 12px;
}
.swatch {
width: 30px;
height: 30px;
border-radius: 15px;
box-shadow: inset 0px 1px 0px rgba(255, 255, 255, 0.5), 0px 2px 2px rgba(0, 0, 0, 0.5);
display: inline-block;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="content">
<span style="color:red;"><b>Content</b> <i>Lorem</i> <font size='7'>ipsum</font> dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et
dolore magna aliqua. Ut enim ad minim</span> veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea
commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla
pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est
laborum.
Content Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et
dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea
commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla
pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est
laborum.
Content Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et
dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea
commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla
pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est
laborum.
Content Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et
dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea
commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla
pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est
laborum.
</div>
<div class="footer">
<div class="items-left">
<div id="colors">
<div class="swatch active" style="background-color: rgba(255,255,131,.5);"></div>
<div class="swatch" style="background-color: rgba(255,140,218,.5);"></div>
<div class="swatch" style="background-color: rgba(144,255,184,.5);"></div>
<div class="swatch clear"></div>
</div>
</div>
</div>
I am trying to adopt a script I have found in this question, but changing the image to some content seems to be harder than I thought.
The script is quite simple and should make the #content div move inside the holder on mousemove:
// written by Roko C. Buljan
var $mmGal = $('#holder'),
$mmImg = $('#content'),
damp = 10, // higher number = smoother response
X = 0, Y = 0,
mX = 0, mY = 0,
wDiff, hDiff,
zeno;
function motion(){
zeno = setInterval(function(){ // Zeno's paradox "catching delay"
X += (mX-X) / damp;
Y += (mY-Y) / damp;
$mmGal.scrollLeft(X*wDiff).scrollTop(Y*hDiff);
}, 26);
}
// Get image size after it's loaded and run our fn
$mmImg.one('load', function() {
wDiff = ( this.width/$mmGal.width() )-1,
hDiff = (this.height/$mmGal.height())-1;
}).each(function() {
if(this.complete) $(this).load();
});
$mmGal.mousemove(function(e) {
mX = e.pageX-this.offsetLeft;
mY = e.pageY-this.offsetTop;
}).hover(function( e ){
return e.type=='mouseenter'? motion() : setTimeout(function(){
clearInterval(zeno);
},1200); // clear if not used
});
Why doesn't the div #content move (text and image)?
Example JSBIN
I have updated the demo and added content on moving background image.
Check the demo:
$(function(){
var $mmGal = $('#mmGal'),
$mmImg = $('#mmImg'),
damp = 10, // higher number = smoother response
X = 0, Y = 0,
mX = 0, mY = 0,
wDiff, hDiff,
zeno;
function motion(){
zeno = setInterval(function(){ // Zeno's paradox "catching delay"
X += (mX-X) / damp;
Y += (mY-Y) / damp;
$mmGal.scrollLeft(X*wDiff).scrollTop(Y*hDiff);
}, 26);
}
// Get image size after it's loaded and run our fn
$mmImg.one('load', function() {
wDiff = ( this.width/$mmGal.width() )-1,
hDiff = (this.height/$mmGal.height())-1;
}).each(function() {
if(this.complete) $(this).load();
});
$mmGal.mousemove(function(e) {
mX = e.pageX-this.offsetLeft;
mY = e.pageY-this.offsetTop;
}).hover(function( e ){
return e.type=='mouseenter'? motion() : setTimeout(function(){
clearInterval(zeno);
},1200); // clear if not used
});
});
*{margin:0;padding:0;}
.main-wrapper {
position: relative;
width:150px;
height:150px;
}
#mmGal{
position:relative;
margin: 20px auto;
width:412px;
height:220px;
overflow:hidden;
background:#eee;
z-index: 0;
}
.content {
position: absolute;
left: 0;
right: 0;
top:0;
color: #ffffff;
padding: 10px;
z-index: 1;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<div class="main-wrapper">
<div class="content">
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. quae molestias ad dolores qui blanditiis, Quas eaque soluta quia ipsa? aliquam?</p>
</div>
<div id="mmGal">
<img id="mmImg" src="http://www.wired.com/images_blogs/rawfile/2013/11/offset_WaterHouseMarineImages_62652-2-660x440.jpg">
</div>
</div>
My original script uses a .load() for the underlay image to load. Since you don't have a background image to move - you don't need necessarily that code part.
Calculate w/hDiff inside the mousemove event.
Also, use jQuery's .outerWidth() and .innerWidth() for the calculation:
jQuery(function($) {
const $mm = $('#holder'),
$mmCont = $('#content'),
damp = 10; // 1 = immediate, higher number = smoother response
let X = 0, Y = 0,
mX = 0, mY = 0,
wDiff = 0, hDiff = 0,
zeno, tOut;
$mm.on({
mousemove(ev) {
wDiff = ($mmCont.innerWidth() / $mm.outerWidth()) -1,
hDiff = ($mmCont.innerHeight() / $mm.outerHeight()) -1;
mX = ev.pageX - this.offsetLeft;
mY = ev.pageY - this.offsetTop;
},
mouseenter() {
clearTimeout(tOut);
clearInterval(zeno);
zeno = setInterval(function() { // Zeno's paradox "catching delay"
X += (mX - X) / damp;
Y += (mY - Y) / damp;
// Use CSS transition:
// $mmCont.css({ transform: `translate(${-X * wDiff}px, ${-Y * hDiff}px)` });
// Use Scroll:
$mm[0].scrollTo(X * wDiff, Y * hDiff);
}, 26);
},
mouseleave() {
// Allow the image to move for some time even after mouseleave
tOut = setTimeout(function() {
clearInterval(zeno);
}, 1200);
}
});
});
#holder {
background: gray;
width: 100%;
position: relative;
overflow: hidden;
height: 180px;
}
#content {
width: 150%;
height: 600px;
background: lightgray;
padding: 40px;
}
<div id="holder">
<div id="content">
<div>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Accusamus dolore impedit dignissimos porro repellendus numquam aut quibusdam, consequuntur modi facere? Totam ut libero corporis sit sequi explicabo ab magni quaerat unde animi aliquid facere
necessitatibus, quae molestias ad dolores qui blanditiis, quisquam minima beatae autem iure. Neque animi tempore iste accusamus ut cum ipsam possimus, perspiciatis quia illo obcaecati sed molestiae amet architecto, nostrum cumque quaerat minima
minus, consequatur rem error nihil. Ipsa eveniet, praesentium suscipit optio blanditiis at, vel illum harum omnis quam. Quas eaque soluta quia ipsa? Illum inventore veritatis facilis eveniet voluptatibus atque laborum necessitatibus adipisci aliquam?</div>
<img src="https://i.stack.imgur.com/BfcTY.jpg">
</div>
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
I have a game where users can chat. Every speech is appended to body as position absolute element with user x position as "left" CSS tag. They are also animated to go to top. I don't want them to have a colliding, vertically or horisontally.
Here's an example:
They don't have to be one on another, but they need to be "one after one"
I have tried a jquery each() and then remove 60 pixel from the current speech position. Here's my code:
var Speech = {
History: [],
New: function(user, text, int) {
var pos = getUserPosition(user).x + 18;
var speech = HTML.Speech.split('\n');
var maxInt = speech.length - 1;
if (int <= maxInt) {
var html = speech[int];
var random = Rand(10);
Speech.pushThemUp();
/** append here ect...
set left position...
$('#speech' + random).css("left", nLeft + "px"); **/
}
},
pushThemUp: function() {
$('.speech').each(function(i, obj) {
var newTop = parseInt($(this).css('top')) - 60;
$(this).css('top', newTop+'px');
});
},
Listener: function() {
var int = setInterval(function() {
$('.speech').each(function(i, obj) {
if(parseInt($(this).css('top')) < 0) {
$(this).remove();
} else {
var newTop = parseInt($(this).css('top')) - 10;
$(this).animate({'top': newTop+'px'});
}
});
}, 1000);
},
getHistory: function() {
return Speech.History;
}
};
Speech.Listener();
module.exports = Speech;
But it doesn't work. They can still have colliding like the example up.
How can I solve that?
Please note: in that example, Speech.Listener() wasn't called.
EDIT: finally, I think my current solution to loop over .speech class and then add top px is good, but why is it animated? Look at the gif, pushThemUp function don't have to animate the speech bubbles but directly edit position, how can I solve that?
I created a code snippet where messages appear and move upwards. When there's no space left, a scrollbar appears.
var box = document.getElementById("box");
var topp = 3;
function post(str) {
var obj = document.createElement("div");
obj.className = "chatObj";
obj.innerHTML = str;
box.appendChild(obj);
box.appendChild(document.createElement("br"));
var width = obj.clientWidth;
var height = obj.clientHeight;
obj.style.marginLeft = (box.clientWidth / 2 - width / 2) + "px";
var x = 15;
obj.style.top = (box.clientHeight - x - height) + "px";
var interval, ctop;
interval = setInterval(function() {
x += 4;
console.log(ctop, topp);
ctop = box.clientHeight - x - height;
obj.style.top = ctop + "px";
if (ctop <= topp) {
obj.style.top = topp + "px";
topp += height + 6;
clearInterval(interval);
}
}, 5);
}
setTimeout(function() {
post("Hi!");
}, 500);
setTimeout(function() {
post("Trollolo!");
}, 1500);
setTimeout(function() {
post("Lorem ipsum dolor sit amet, timeam aliquando ei cum, putent possim in usu, at causae pericula petentium has. In mea legere salutatus voluptaria. No vix ancillae accusata. Nec meis probo noster eu, ius no quas audire.<br><br>Qui quem nominavi ei. Pri nisl eirmod id. Has cetero vocent abhorreant no, at mei altera expetendis. Has id novum aeterno salutatus.<br><br>Prompta offendit et eos, eos an admodum comprehensam, ex velit doming dolorem mei. At has dolor alterum laoreet, id duo tollit libris contentiones. An mel recteque omittantur dissentiet, ex nam novum iuvaret. Per id alterum habemus gubergren.<br><br>Nulla possim mea in. Vis et postulant constituam. Viris vulputate vituperatoribus eu usu, wisi meis ex his. Prompta accumsan cum et, possim eligendi omittantur sed id. Eos ad nemore integre recusabo, agam doctus viderer ei pri, cu eius nonumes senserit vis. Qui iudico causae te.<br><br>Eam ne mandamus evertitur, case adversarium neglegentur duo ex, no cum nominati forensibus. Et vel putant deleniti. Illum aliquando voluptatibus per no, ei quo albucius phaedrum. Cu lorem appetere percipit sed, ubique epicuri ad eos, te eos diam nusquam persecuti. Eu qui meis illum eleifend, eam veniam vivendo no, nisl fierent in quo.");
}, 2500);
#box {
display: block;
position: absolute;
width: 100%;
height: 100%;
left: 0;
top: 0;
background-color: tomato;
overflow: auto;
}
#box .chatObj {
position: absolute;
display: inline-block;
max-width: 80%;
background-color: white;
border-radius: 2px;
padding: 3px 6px;
margin: 3px 0;
box-shadow: 0 0 2px 1px rgba(0, 0, 0, 0.1);
font-family: Arial, sans-serif;
font-size: 14px;
}
#box .chatObj::after {
display: block;
position: absolute;
content: ".";
color: transparent;
height: 6px;
}
<div id="box"></div>
If you use position: relative and a wrapper element with display: block, you can get exactly what you want without having to worry about collision detection. The only thing you'll need to do is calculate the initial top value based on the players position minus the initial top position of the bubble (since using position: relative the bubbles will be added to the DOM bellow the last bubble).
See this simple jsfiddle. https://jsfiddle.net/straker/0n6jbupg/