I have a page whose background image scales as the browser re-sizes. What I'm trying to figure out is how to make the blue marker image scale and maintain position in proportion to the background. For example, if the blue marker were on the tip of the harbor, as I scaled the browser down, I'd want that marker to stay on the tip of the harbor and shrink in size proportionally with the browser's new dimensions.
Anyone have any ideas that will point me in the right direction?
I used the below method to find the browser size, but I think my math's a little off. You can view the live example here:
http://mikeheavers.com/stage/full_screen_map/
The marker gets really off if you scale it in one direction more than the other.
<style type="text/css">
html {
background: url(images/antigua.jpeg) no-repeat center center fixed;
-webkit-background-size: cover;
-moz-background-size: cover;
-o-background-size: cover;
background-size: cover;
}
#infoDiv {
background: white;
padding: 20px;
width: 200px;
position:absolute;
}
#map_canvas {
position:absolute ;
}
.marker {
position:absolute;
left: 800px;
top: 400px;
height: 20px;
width: 20px;
background: #ff0000;
}
</style>
<script type="text/javascript" charset="utf-8" src="js/jquery-1.4.4.min.js"></script>
<div id="infoDiv">
</div>
<div id="markers">
<div class="marker">
</div>
</div>
<script type="text/javascript">
$(document).ready(function()
{
var myWidth = window.innerWidth;
var leftVal = $("#markers").children().css('left');
var leftValNumber = parseFloat(leftVal);
var leftRatio = leftValNumber / myWidth;
var leftValPos;
var myHeight = window.innerHeight;
var topVal = $("#markers").children().css('top');
var topValNumber = parseFloat(topVal);
var topRatio = topValNumber / myHeight;
var topValPos;
var scaleRatio;
if (myWidth > myHeight){
scaleRatio = 20/myWidth;
} else {
scaleRatio = 20/myHeight;
}
window.onresize = function() {
sizeMarkers();
}
function init()
{
sizeMarkers();
}
function sizeMarkers()
{
if( typeof( window.innerWidth ) == 'number' ) {
//Non-IE
myWidth = window.innerWidth;
myHeight = window.innerHeight;
} else if( document.documentElement && ( document.documentElement.clientWidth || document.documentElement.clientHeight ) ) {
//IE 6+ in 'standards compliant mode'
myWidth = document.documentElement.clientWidth;
myHeight = document.documentElement.clientHeight;
} else if( document.body && ( document.body.clientWidth || document.body.clientHeight ) ) {
//IE 4 compatible
myWidth = document.body.clientWidth;
myHeight = document.body.clientHeight;
}
topVal = $("#markers").children().css('top');
topValNumber = parseFloat(topVal);
topValPos = topRatio * myHeight;
leftVal = $("#markers").children().css('top');
leftValNumber = parseFloat(leftVal);
leftValPos = leftRatio * myWidth;
if (myWidth < myHeight){
$("#markers").children().width(myWidth*scaleRatio);
$("#markers").children().height(myWidth*scaleRatio);
} else {
$("#markers").children().width(myHeight*scaleRatio);
$("#markers").children().height(myHeight*scaleRatio);
}
$("#markers").children().css('top',topValPos);
$("#markers").children().css('left',leftValPos);
$("#infoDiv").html( 'Width = ' + myWidth + ' | Height = ' + myHeight + ' | Top Value: ' + topValPos + ' | Left Value: ' + leftValPos);
}
init();
});
</script>
You could adjust width of your marker using JavaScript by keeping at any onresize events the same ratio marker-width and page-width.
To know your viewport width :
http://www.howtocreate.co.uk/tutorials/javascript/browserwindow
Hope it helps.
Related
I made a website with three.js for 3d work.
It's concept is universe that has so many texts.
There is 2 things I have to implement.
First. Object is star when distance between camera and object is far.
Second. Object is Text when distance between camera and object is close.
So, I implement it with LOD.
Everything works fine.
But, star <-> text change trainsition is so rough.
I want to insert smooth change transition(animation) into it.
So I read a docs, sadly, there is any properties about it.
Is it possible to insert animation into LOD?
or any possible soulution about this?
[Source code]
// VARIABLES
let clock, camera, scene, renderer, mixer;
var myElement = document.getElementById("threejs");
const mouse = new THREE.Vector2();
const clicked = new THREE.Vector2();
const target = new THREE.Vector2();
const windowHalf = new THREE.Vector2( window.innerWidth / 2, window.innerHeight / 2 );
const moveState = {forward: 0, back: 0};
var isMobile = false;
var textCount = 200;
checkMobile()
init();
function init() {
// CAMERA
camera = new THREE.PerspectiveCamera( 45, window.innerWidth / window.innerHeight, 1, 2100 );
camera.position.x = 0;
camera.position.y = 0;
camera.position.z = 1200;
scene = new THREE.Scene();
scene.fog = new THREE.Fog(0xf05fa6, 300, 400);
clock = new THREE.Clock();
// HELPER
const gridHelper = new THREE.PolarGridHelper( 8, 16 );
scene.add( gridHelper );
// LIGHT
const ambientLight = new THREE.AmbientLight( 0xffffff, 0.2 );
scene.add( ambientLight );
const directionalLight = new THREE.DirectionalLight( 0xffffff, 0.8 );
directionalLight.position.set( 1, 1, - 1 );
scene.add( directionalLight );
// CONTROLS
if(isMobile) {
var controls = new THREE.DeviceOrientationControls(camera);
console.log('isMobile true');
} else {
console.log('isMobile false');
}
// ADD MESH
var group = new THREE.Group();
var size = ( isMobile ? 2 : 4 );
var starsLights = new THREE.Group();
for ( let i = 0; i < textCount; i ++ ) {
var lod = new THREE.LOD();
// Star
var geometry = new THREE.SphereGeometry(0.3, 16, 16);
var material = new THREE.MeshBasicMaterial({color: 0xffffff});
var star = new THREE.Mesh(geometry, material);
// Text
let sprite = new THREE.TextSprite({
textSize: size,
redrawInterval: 250,
texture: {
text: 'TEST TEST TEST',
fontFamily: 'Arial, Helvetica, sans-serif',
},
material: {
color: 'white',
},
});
// Star Light
var velX = (Math.random() + 0.1) * 0.1 * (Math.random()<0.5?-1:1);
var velY = (Math.random() + 0.1) * 0.1 * (Math.random()<0.5?-1:1);
star.vel = new THREE.Vector2(velX, velY);
var starLight = new THREE.PointLight(0x000000, 0.8, 3);
starLight.position.copy(star.position);
starLight.position.y += 0.5;
starsLights.add(starLight);
// Add
lod.addLevel(sprite, 1);
lod.addLevel(star, 200);
lod.position.x = Math.random() * 180-100;
lod.position.y = Math.random() * 180-100;
lod.position.z = Math.random() * 1000-40;
group.add(lod);
// group.add(starsLights);
}
scene.add(group);
scene.add(starLight);
// Renderer
renderer = new THREE.WebGLRenderer( { antialias: true, alpha: true } );
renderer.setPixelRatio( window.devicePixelRatio );
renderer.setSize( window.innerWidth, window.innerHeight );
document.getElementById("universe").appendChild(renderer.domElement);
// Event handler
window.addEventListener( 'resize', onWindowResize, false );
document.addEventListener('mousemove', onMouseMove, false);
document.addEventListener('mousewheel', onMouseWheel, false);
document.addEventListener('contextmenu', onContextMenu, false);
function animate() {
target.x = ( 1 - mouse.x ) * 0.002;
target.y = ( 1 - mouse.y ) * 0.002;
camera.rotation.x += 0.05 * ( target.y - camera.rotation.x );
camera.rotation.y += 0.05 * ( target.x - camera.rotation.y );
if(isMobile) {
controls.update();
}
// Object change related to distance
group.children.forEach(function(child) {
child.update(camera);
var distance = camera.position.distanceTo(child.position);
var opacity = -1 / 400 * distance + 1;
if (opacity < 0) {
opacity = 0;
}
child.getObjectForDistance(distance).material.opacity = opacity;
})
// Render
requestAnimationFrame( animate );
render(scene, camera);
}
animate();
}
function onWindowResize() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize( window.innerWidth, window.innerHeight );
}
function onMouseWheel(event) {
if(camera.position.z > 100) {
event.preventDefault();
camera.position.z -= event.deltaY * 0.2;
} else {
}
}
function render() {
const delta = clock.getDelta();
if ( mixer !== undefined ) mixer.update( delta );
renderer.render( scene, camera );
}
function onTransitionEnd( event ) {
console.log("Loading Complete");
event.target.remove();
}
// Exist functions
function checkMobile() {
var UserAgent = navigator.userAgent;
if (UserAgent.match(/iPhone|iPod|Android|Windows CE|BlackBerry|Symbian|Windows Phone|webOS|Opera Mini|Opera Mobi|POLARIS|IEMobile|lgtelecom|nokia|SonyEricsson/i) != null || UserAgent.match(/LG|SAMSUNG|Samsung/) != null) {
isMobile = true;
} else {
isMobile = false;
}
}
function onMouseMove(event) {
mouse.x = ( (event.clientX/2) - (windowHalf.x/2) );
mouse.y = ( (event.clientY/2) - (windowHalf.y/2) );
clicked.x = ( event.clientX / window.innerWidth ) * 2 - 1;
clicked.y = - ( event.clientY / window.innerHeight ) * 2 + 1;
}
function onResize(event) {
const width = window.innerWidth;
const height = window.innerHeight;
windowHalf.set( width / 2, height / 2 );
camera.aspect = width / height;
camera.updateProjectionMatrix();
renderer.setSize( width, height );
}
function onContextMenu(event) {
event.preventDefault();
}
function topFunction() {
document.body.scrollTop = 0;
document.documentElement.scrollTop = 0;
}
canvas {
width: 100%;
height: 100%;
/* background: #11e8bb; Old browsers */
/* background: -moz-linear-gradient(top, #11e8bb 0%, #8200c9 100%); FF3.6-15 */
/* background: -webkit-linear-gradient(top, #11e8bb 0%,#8200c9 100%); Chrome10-25,Safari5.1-6 */
/* background: linear-gradient(to bottom, #11e8bb 0%,#8200c9 100%); W3C, IE10+, FF16+, Chrome26+, Opera12+, Safari7+ */
background: radial-gradient(circle, #ed1654, #f61e6c, #f76098);
filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#11e8bb', endColorstr='#8200c9',GradientType=0 ); /* IE6-9 */
}
body {
margin: 0;
}
canvas {
width: 100%;
height: 100%;
}
#threejs {
position: absolute;
overflow: hidden;
width: 100%;
height: 100%;
}
header {
position: fixed;
z-index: 9999;
background-color: white;
width: 100%;
top: 0;
display: flex;
align-items: center;
height: 50px;
}
/* Header Left */
.header-left {
display: flex;
justify-content: center;
flex: 1;
}
.header-left img {
width: 80px;
height: 20px;
}
/* Header Right */
.header-right {
flex: 1;
padding-left: 200px;
}
.header-right a {
text-decoration: none;
color: black;
font-weight: 600;
}
.header-right a:nth-child(2) {
padding-left: 50px;
padding-right: 50px;
}
/* Main Company */
.down-btn {
display: flex;
position: absolute;
justify-content: center;
align-items: center;
bottom: 0;
color: white;
left: 50%;
font-size: 2rem;
cursor: pointer;
}
.down-btn a {
text-decoration: none;
color: white;
padding-bottom: 20px;
}
/* Section */
section {
background-color: aliceblue;
height: 100%;
}
<html>
<head>
<link rel="stylesheet" type="text/css" href="style.css">
<!-- <script src="three.js"></script>-->
<script src="https://cdn.rawgit.com/mrdoob/three.js/master/build/three.min.js"></script>
<script src="https://cdn.rawgit.com/mrdoob/three.js/master/examples/js/controls/OrbitControls.js"></script>
<script src="https://unpkg.com/three.texttexture"></script>
<script src="https://unpkg.com/three.textsprite"></script>
<meta name="viewport" content="user-scalable=no, width=device-width, initial-scale=1, maximum-scale=1">
</head>
<body>
<header>
<div class="header-left">
MAIN
</div>
<div class="header-right">
ABOUT
PRODUCT
CONTACT
</div>
</header>
<section id="universe"></section>
<script src="src.js"></script>
<section id="section">
SECTION
</section>
</body>
</html>
THREE.LOD can't do what you are looking for. But it's not that hard to write a custom LOD class that performs a smooth alpha blending between two levels. The advantage of this approach is that it is perceived as more continuous than discrete LODs. However, since two objects are rendered during the transition, it's computationally a bit more expensive.
The following fiddle illustrates this approach (I'm sure it's possible to implement this more elegant but it's at least a starting point^^): https://jsfiddle.net/f2Lommf5/14585/
When creating a LOD object, you can adjust a threshold value that defines the extent of the transition.
const lod = new LOD();
lod.threshold = 250;
I made a graphical website with three.js.
It's concept is universe that has so many texts.
When distance of between camera and mesh is close, mesh is text.
But if distance is far, it change to square.
I wonder that change mesh related to distance is possible?
I searched in google few hours, there is no information about this.
code here:
// Define Variables
var myElement = document.getElementById("threejs");
let camera, scene, renderer;
const mouse = new THREE.Vector2();
clicked = new THREE.Vector2();
const target = new THREE.Vector2();
const windowHalf = new THREE.Vector2( window.innerWidth / 2, window.innerHeight / 2 );
const moveState = {forward: 0, back: 0};
var isMobile = false;
var hold = -1;
/****** Define Function ******/
/*****************************/
checkMobile = () => {
var UserAgent = navigator.userAgent;
if (UserAgent.match(/iPhone|iPod|Android|Windows CE|BlackBerry|Symbian|Windows Phone|webOS|Opera Mini|Opera Mobi|POLARIS|IEMobile|lgtelecom|nokia|SonyEricsson/i) != null || UserAgent.match(/LG|SAMSUNG|Samsung/) != null) {
isMobile = true;
} else {
isMobile = false;
}
}
checkMobile();
onMouseMove = (event) => {
mouse.x = ( (event.clientX/2) - (windowHalf.x/2) );
mouse.y = ( (event.clientY/2) - (windowHalf.y/2) );
clicked.x = ( event.clientX / window.innerWidth ) * 2 - 1;
clicked.y = - ( event.clientY / window.innerHeight ) * 2 + 1;
}
onResize = (event) => {
const width = window.innerWidth;
const height = window.innerHeight;
windowHalf.set( width / 2, height / 2 );
camera.aspect = width / height;
camera.updateProjectionMatrix();
renderer.setSize( width, height );
}
onContextMenu = (event) => {
event.preventDefault();
}
onMouseDown = (event) => {
hold = event.which;
}
onMouseUp = () => {
hold = -1;
};
// TEST
//
// Start Script
init = () => {
camera = new THREE.PerspectiveCamera( 45, window.innerWidth / window.innerHeight, 1, 5000 );
camera.position.x = 0;
camera.position.y = 0;
camera.position.z = 1000;
scene = new THREE.Scene();
const geometry = new THREE.BoxBufferGeometry();
const material = new THREE.MeshNormalMaterial({ transparent: true });
if(isMobile) {
var controls = new THREE.DeviceOrientationControls(camera);
} else {
console.log('isMobile false');
}
group = new THREE.Group();
for ( let i = 0; i < 800; i ++ ) {
let sprite = new THREE.TextSprite({
textSize: 2,
redrawInterval: 1,
texture: {
text: 'TEST',
fontFamily: 'Arial, Helvetica, sans-serif',
},
material: {
color: 'white',
},
});
sprite.position.x = Math.random() * 180-100;
sprite.position.y = Math.random() * 180-100;
sprite.position.z = Math.random() * 1000-40;
group.add(sprite);
}
scene.add(group);
renderer = new THREE.WebGLRenderer( { antialias: true, alpha: true } );
renderer.setSize( window.innerWidth, window.innerHeight );
document.body.appendChild( renderer.domElement );
// Event handler
document.addEventListener('mousemove', onMouseMove, false);
document.addEventListener('mousedown', onMouseDown, false);
document.addEventListener('mouseup', onMouseUp, false);
document.addEventListener('contextmenu', onContextMenu, false);
window.addEventListener('resize', onResize, false);
// Helper
var axesHelper = new THREE.AxesHelper( 15 );
scene.add( axesHelper );
animate = () => {
// For camera follow mouse cursor
target.x = ( 1 - mouse.x ) * 0.002;
target.y = ( 1 - mouse.y ) * 0.002;
camera.rotation.x += 0.05 * ( target.y - camera.rotation.x );
camera.rotation.y += 0.05 * ( target.x - camera.rotation.y );
if(isMobile) {
controls.update();
}
switch(hold) {
case 1:
if(camera.position.z > 0) {
camera.position.z -= 4;
}
break;
case 3:
camera.position.z += 4;
break;
}
// Object opacity related to distance between camera and object
for (i = 0; i < 800; i++) {
var distance = camera.position.distanceTo(group.children[i].position);
var opacity = -1 / 400 * distance + 1;
if (opacity < 0) {
opacity = 0;
}
group.children[i].material.opacity = opacity;
}
requestAnimationFrame( animate );
renderer.render( scene, camera );
}
// Run
animate();
}
// Run
init();
canvas {
width: 100%;
height: 100%;
/* background: #11e8bb; Old browsers */
/* background: -moz-linear-gradient(top, #11e8bb 0%, #8200c9 100%); FF3.6-15 */
/* background: -webkit-linear-gradient(top, #11e8bb 0%,#8200c9 100%); Chrome10-25,Safari5.1-6 */
/* background: linear-gradient(to bottom, #11e8bb 0%,#8200c9 100%); W3C, IE10+, FF16+, Chrome26+, Opera12+, Safari7+ */
background: radial-gradient(circle, #ed1654, #f61e6c, #f76098);
filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#11e8bb', endColorstr='#8200c9',GradientType=0 ); /* IE6-9 */
}
body {
margin: 0;
}
canvas {
width: 100%;
height: 100%;
}
#threejs {
position: absolute;
overflow: hidden;
width: 100%;
height: 100%;
}
header {
position: fixed;
z-index: 9999;
background-color: white;
width: 100%;
top: 0;
display: flex;
align-items: center;
height: 50px;
}
/* Header Left */
.header-left {
display: flex;
justify-content: center;
flex: 1;
}
.header-left img {
width: 80px;
height: 20px;
}
/* Header Right */
.header-right {
flex: 1;
padding-left: 200px;
}
.header-right a {
text-decoration: none;
color: black;
font-weight: 600;
}
.header-right a:nth-child(2) {
padding-left: 50px;
padding-right: 50px;
}
/* Main Company */
.down-btn {
display: flex;
position: absolute;
justify-content: center;
align-items: center;
bottom: 0;
color: white;
left: 50%;
font-size: 2rem;
cursor: pointer;
}
.down-btn a {
text-decoration: none;
color: white;
padding-bottom: 20px;
}
/* Section */
section {
background-color: aliceblue;
height: 100%;
}
<html>
<head>
<link rel="stylesheet" type="text/css" href="style.css">
<!-- <script src="three.js"></script>-->
<script src="https://cdn.rawgit.com/mrdoob/three.js/master/build/three.min.js"></script>
<script src="https://cdn.rawgit.com/mrdoob/three.js/master/examples/js/controls/OrbitControls.js"></script>
<script src="https://unpkg.com/three.texttexture"></script>
<script src="https://unpkg.com/three.textsprite"></script>
<meta name="viewport" content="user-scalable=no, width=device-width, initial-scale=1, maximum-scale=1">
</head>
<body>
<header>
<div class="header-left">
MAIN
</div>
<div class="header-right">
ABOUT
PRODUCT
CONTACT
</div>
</header>
<div id="threejs"></div>
<script src="src.js"></script>
<div class="down-btn">
↓
</div>
<section id="section">
SECTION
</section>
</body>
</html>
(Mouse left click : move forward / right click : move backward)
I implemented almost done except change mesh.
Is it possible or any solution about this issue?
Thanks.
You can use the THREE.LOD() (Level of Detail) to replace the a mesh by another mesh at a certain distance.
The example (https://threejs.org/examples/#webgl_lod) uses only same type of geometry for the different distances. But if you look into the code, each distance has its own geometry and mesh instance.
And so can you, to change to 100% different mesh.
// create meshes and LOD objects
for ( let i = 0; i < 800; i ++ ) {
let lod = new THREE.LOD();
let sprite = new THREE.TextSprite(...);
let squareGeo = new THREE.PlaneBufferGeometry(2,2),
squareMat = new THREE.MeshBasicMaterial(),
square = new THREE.Mesh(squareGeo, squareMat);
lod.addLevel(sprite, 1);
lod.addLevel(square, 100); // will be visible from 100 and beyond
lod.position.x = Math.random() * 180-100;
lod.position.y = Math.random() * 180-100;
lod.position.z = Math.random() * 1000-40;
group.add(lod);
}
// animation loop
function animate() {
// ...
group.children.forEach(function (child) {
// LOD update
child.update(camera);
// opacity
var distance = camera.position.distanceTo(child.position);
var opacity = -1 / 400 * distance + 1;
if (opacity < 0) {
opacity = 0;
}
child.getObjectForDistance(distance).material.opacity = opacity;
});
requestAnimationFrame( animate );
renderer.render( scene, camera );
}
EDIT: LOD class modification for smooth transition
addLevel: function ( object, distance, fadeDistance ) {
...
levels.splice( l, 0, { distance: distance, fadeDistance: fadeDistance || distance, object: object } );
...
}
update: function () {
...
levels[ 0 ].object.visible = true;
levels[ 0 ].object.material.opacity = 1.0;
for ( var i = 1, l = levels.length; i < l; i ++ ) {
if ( distance >= levels[ i ].distance && distance < levels[ i ].fadeDistance ) {
levels[ i ].object.visible = true;
var t = (distance - levels[i].distance) / (levels[i].fadeDistance - levels[i].distance);
levels[ i - 1 ].object.material.opacity = 1.0 - t;
levels[ i ].object.material.opacity = t;
} else if ( distance >= levels[ i ].fadeDistance ) {
levels[ i - 1 ].object.visible = false;
levels[ i ].object.visible = true;
levels[ i ].object.material.opacity = 1.0;
} else {
break;
}
}
...
}
Of course, material.transparent property of objects should be set true, so opacity will work.
Adding square to LOD object
lod.addLevel(sprite, 1);
lod.addLevel(star, 100, 140); // will fade in at distance of 100 to 140, fully visible beyond 140
lod.addLevel(dummy, 200, 400); // if dummy is an object with empty geometry, star will fade out between 200 and 400
Remove opacity modification in animation loop.
I have a rectangle. I want to split it into a number of nonoverlapping smaller rectangles. Any good data structure to represent the partition?
Ok this could be interesting, how about:
var w = $('#rect').width();
var h = $('#rect').height();
var ratio = h/w;
var splitAcross = 10;//Ten divisions squared *Changeable*
var splitW = w/splitAcross;
var splitH = ratio*splitW;//Split across and down
var divRects = document.createElement("div"); // Create with DOM
divRects.style.width = splitW+'px';
divRects.style.height = splitH+'px';
var span = 0;
var spl = 0;
while (span < splitAcross)
{
spl++;
var cln = divRects.cloneNode(true);
if (splitW*spl % w == 0) span++;
$("#rect").append(cln);
}
//Removeable*** this is just to show divisions
$("#rect div").each(function (){
$(this).animate({
left: ($(this).offset().left-(w/2))*0.1,
top: ($(this).offset().top-(h/2))*0.1
}, 1000).animate({
left: 0,
top: 0
}, 1000);
});
//Removeable
#rect div {
border: 1px solid #eee;
margin: -1px;
float: left;
position: relative;
background:#5fba7d;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="rect" style="width:450px; height:200px"></div>
<!--width:450px; height:200px *Changeable*-->
I have few ckeditor-s on my page, editors work in iframe mode, they are not inline. Each of them has autogrow option turned on. So sometimes content of the editor is higher than screen and toolbar is not visible. This obviously creates usability issues for people working with editor.
To solve this I would like to keep toolbar of currently active editor on the screen. The only problem I am not sure where should I start.
Few things I've figured out already:
1) It cannot be solved with CSS-only, as long as I need toolbar be fixed only for active editor and when its toolbar is not on the screen
2) I would rather create some CKeditor plugin than creating external code that controls scroll position and moves cke_toolbox basing on that.
What would you suggest?
I think I found solution that works for me.
JS code (updated):
$(function () {
if (typeof CKEDITOR === 'undefined') {
return;
}
var floatingClass = 'floatingToolbox';
var $editors;
CKEDITOR.on('instanceReady', function (e) {
$editors = $('.cke', e.element);
e.editor.on('focus',function() {
checkToolbars($editors, floatingClass);
$(window).on('scroll.ckeditor', function () {
checkToolbars($editors, floatingClass);
});
});
e.editor.on('blur', function () {
$(window).unbind('scroll.ckeditor');
$('.cke_toolbox', $editors).removeClass(floatingClass);
});
});
});
function checkToolbars($editors, floatingClass) {
if (!$editors)
return;
var editor = $editors.filter('.cke_focus');
if (editor.length == 0)
return;
var toolbox = $('.cke_toolbox', editor);
var offset = editor.offset();
var top = offset.top;
var bottom = offset.top + editor.height() - 55;
var scrollPosition = $(window).scrollTop();
if (top < scrollPosition && bottom > scrollPosition) {
toolbox.addClass(floatingClass).css(
{
left: (offset.left + 1) + 'px',
width: editor.width() + 'px'
});
} else if (toolbox.hasClass(floatingClass)) {
toolbox.removeClass(floatingClass);
}
}
CSS:
.floatingToolbox {
background-color: #cce4fb !important;
background-image: -webkit-gradient(linear, left top, left bottom, from(#f9fcfe), to(#cce4fb)) !important;
background-image: -moz-linear-gradient(top, #f9fcfe, #cce4fb) !important;
background-image: -webkit-linear-gradient(top, #f9fcfe, #cce4fb) !important;
background-image: -o-linear-gradient(top, #f9fcfe, #cce4fb) !important;
background-image: -ms-linear-gradient(top, #f9fcfe, #cce4fb) !important;
background-image: linear-gradient(top, #f9fcfe, #cce4fb) !important;
border-bottom: 1px solid #b7cde1 !important;
border-top: 1px solid #b7cde1 !important;
box-sizing: border-box;
display: block;
padding: 5px 5px 0 5px !important;
position: fixed;
top: 29px;
z-index: 10000;
}
Made a plugin for this purpose.
Create sticky/plugin.js in CKEditor plugins folder.
Enable the plugin adding the following code inside your config.js.
plugin.js
CKEDITOR.plugins.add( 'sticky', {
init: function( editor ) {
setToolbars();
['scroll', 'click'].forEach(function(e) {
window.addEventListener(e, function(){
setToolbars();
}, false);
});
editor.on('contentDom', function () {
var editable = editor.editable();
editable.attachListener(editable, 'click', function () {
setToolbars();
});
});
function setToolbars() {
document.querySelectorAll(".cke").forEach(function(editor) {
let inner = editor.querySelector('.cke_inner'),
content = editor.querySelector('.cke_contents'),
toolbar = editor.querySelector('.cke_top');
let scrollTop = window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop;
function getCoords(elem) { // crossbrowser version
let box = elem.getBoundingClientRect(),
body = document.body,
docEl = document.documentElement,
scrollTop = window.pageYOffset || docEl.scrollTop || body.scrollTop,
scrollLeft = window.pageXOffset || docEl.scrollLeft || body.scrollLeft,
clientTop = docEl.clientTop || body.clientTop || 0,
clientLeft = docEl.clientLeft || body.clientLeft || 0,
top = box.top + scrollTop - clientTop,
left = box.left + scrollLeft - clientLeft;
return { top: Math.round(top), left: Math.round(left) };
}
inner.style.position = "relative";
toolbar.style.position = "absolute";
toolbar.style.width = "100%";
toolbar.style.left = "0";
toolbar.style.margin = "0";
toolbar.style.boxSizing = "border-box";
let editorTop = getCoords(editor).top;
let editorHeight = editor.offsetHeight;
let toolbarHeight = toolbar.offsetHeight;
content.style.paddingTop = toolbarHeight+"px";
let contentHeight = content.offsetHeight;
let editorBorderTopWidth = parseFloat(getComputedStyle(editor).borderTopWidth);
if((scrollTop > editorTop) && (scrollTop < (editorTop+contentHeight-toolbarHeight))) {
toolbar.style.top = (scrollTop-editorTop-editorBorderTopWidth) + "px";
} else if (scrollTop >= (editorTop+contentHeight-toolbarHeight)) {
toolbar.style.top = (contentHeight-toolbarHeight-editorBorderTopWidth) + "px";
} else {
toolbar.style.top = "0";
}
});
}
}
});
Also created plugin page on GitHub.
I faced the same issue,
if you have header then below css will also help
#media only screen and (max-width: 767px) {
.ck-sticky-panel__content {
top: 180px !important;
}
}
#media only screen and (min-width: 768px) {
.ck-sticky-panel__content {
top: 128px !important;
}
}
DcpCkEditor.create(document.querySelector(`#${editorId}`), {
...editorConfiguration,
initialData,
}).then((editor: any) => {
document.getElementById('main')?.addEventListener('scroll', () => {
setTimeout(() => {
// eslint-disable-next-line no-underscore-dangle
editor.ui.view.stickyPanel._checkIfShouldBeSticky()
}, 100)
})
})
UPDATE: Got a jsfiddle up # http://jsfiddle.net/gacDy/7/ , however, I can't even seem to get this working correctly. I'll keep working on it, but hopefully it'll help.
I'm using Hammer.js for an app I'm working on. For now, I've got a demo that I got on the web somewhere that I'm using to figure out how to make this work. For the demo I have two images (I'll post screenshots below) that I'm trying to get to zoom independently with hammer.js.
As things are right now, both images zoom, but the second one zooms into the region where the first image is. (See screenshots below)
Pre-zoom image:
Post-zoom image:
Code for images:
<div id="zoom2">
<img src="Chrysanthemum.jpg" alt="" width="600" height="377" />
</div>
<div id="zoom">
<img src="waldo.jpg" alt="" width="600" height="377" />
</div>
Functions and such:
<div id="debug"></div>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1/jquery.min.js"></script>
<script src="hammer.js"></script>
<script src="jquery.hammer.js"></script>
<script src="jquery.specialevent.hammer.js"></script>
<script>
var debug_el = $("#debug");
// disable the dragging of images on desktop browsers
$("img").bind("dragstart", function() {
return false;
});
$(function(){
var zoom = new ZoomView('#zoom','#zoom :first');
});
function ZoomView(container, element) {
container = $(container).hammer({
prevent_default: true,
scale_treshold: 0,
drag_min_distance: 0
});
element = $(element);
// prefixes
var vendorPrefixes = ["", "-webkit-", "-moz-", "-o-", "-ms-", "-khtml-"];
var displayWidth = container.width();
var displayHeight = container.height();
//These two constants specify the minimum and maximum zoom
var MIN_ZOOM = 1;
var MAX_ZOOM = 5;
var scaleFactor = 1;
var previousScaleFactor = 1;
//These two variables keep track of the X and Y coordinate of the finger when it first
//touches the screen
var startX = 0;
var startY = 0;
//These two variables keep track of the amount we need to translate the canvas along the X
//and the Y coordinate
var translateX = 0;
var translateY = 0;
//These two variables keep track of the amount we translated the X and Y coordinates, the last time we
//panned.
var previousTranslateX = 0;
var previousTranslateY = 0;
//Translate Origin variables
var tch1 = 0,
tch2 = 0,
tcX = 0,
tcY = 0,
toX = 0,
toY = 0,
cssOrigin;
var last_drag_event;
container.bind("transformstart", function(event){
//We save the initial midpoint of the first two touches to say where our transform origin is.
tch1 = [event.touches[0].x, event.touches[0].y];
tch2 = [event.touches[1].x, event.touches[1].y];
tcX = (tch1[0]+tch2[0])/2;
tcY = (tch1[1]+tch2[1])/2;
toX = tcX;
toY = tcY;
cssOrigin = toX +"px "+ toY +"px";
});
container.bind("transform", function(event) {
scaleFactor = previousScaleFactor * event.scale;
scaleFactor = Math.max(MIN_ZOOM, Math.min(scaleFactor, MAX_ZOOM));
transform(event);
});
container.bind("transformend", function(event) {
previousScaleFactor = scaleFactor;
});
container.bind("drag", function(event) {
cssOrigin = (toX + (toX-event.position.x) / scaleFactor) +"px " +
(toY + (toY-event.position.y) / scaleFactor) +"px";
transform(event);
last_drag_event = event;
});
container.bind("dragend", function(event) {
toX += ((toX - last_drag_event.position.x) / scaleFactor);
toY += ((toY - last_drag_event.position.y) / scaleFactor);
cssOrigin = toX +"px "+ toY +"px";
transform(event);
debug_el.text('TX: '+toX+' TY: '+toY);
});
function transform(event) {
//We're going to scale the X and Y coordinates by the same amount
var cssScale = "scale("+ scaleFactor +")";
var props = {};
$(vendorPrefixes).each(function(i, vendor) {
props[vendor +"transform"] = cssScale;
props[vendor +"transform-origin"] = cssOrigin;
});
element.css(props);
debug_el.text('TX: '+translateX+' TY: '+translateY+' '+element.css('-webkit-transform-origin'))
}
}
</script>
-- I duplicated the above code and changed any references to the first image to reference the second image in order to make the second image zoomable. But like I mentioned it's not working correctly. I've tried changing around all my variable names in case I missed a reference to the 1st image that could be causing this overlap but nothing has helped.
EDIT: This may also be pertinent:
<head>
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
<style>
#zoom {
height: 377px;
width: 600px;
overflow: hidden;
position:relative;
background: #eee;
border: solid 1px #ccc;
}
</style>
<meta name="viewport2" content="width=device-width, initial-scale=1.0, user-scalable=yes">
<style>
#zoom2 {
height: 377px;
width: 600px;
overflow: hidden;
position:static;
background: #eee;
border: solid 1px #ccc;
}
</style>
I just need help figuring out how to keep the second image from disappearing into the 1st image when zooming. I'd appreciate any help I can get. Thanks all.