I was looking for a fix/solution for that and found this topic:
http://highslide.com/forum/viewtopic.php?t=3030
function fixElement(el) {
var stl = el.style;
stl.position = 'fixed';
stl.top = (parseInt(stl.top) - hs.page.scrollTop) +'px';
stl.left = (parseInt(stl.left) - hs.page.scrollLeft) +'px';
}
function unfixElement(el) {
var stl = el.style;
stl.position = 'absolute';
stl.top = (parseInt(stl.top) + hs.page.scrollTop) +'px';
stl.left = (parseInt(stl.left) + hs.page.scrollLeft) +'px';
}
if (!hs.ie || hs.ieVersion() > 6) {
hs.Expander.prototype.onAfterExpand = function() {
fixElement (this.wrapper);
if (this.outline) fixElement(this.outline.table);
};
hs.Expander.prototype.onBeforeClose = function() {
unfixElement (this.wrapper);
if (this.outline) unfixElement(this.outline.table);
};
}
Got a small hack on the forum topic, but the hack does not work on any Internet Explorer that I tried (IE7, IE8 and IE9).
Does anyone has a "fix" on the hack to make it work on IE?
I think it's something related on this part of the code, this condition:
if (!hs.ie || hs.ieVersion() > 6)
I removed and it worked, but maybe this code could be changed.
Thank you.
The below code is what you need. Live demo: http://www.highslide.com/studies/position-fixed.html
Note: requires highslide-full.js
// Highslide fixed popup mod. Requires the "Events" component.
if (!hs.ie || hs.uaVersion > 6) hs.extend ( hs.Expander.prototype, {
fix: function(on) {
var sign = on ? -1 : 1,
stl = this.wrapper.style;
if (!on) hs.getPageSize(); // recalculate scroll positions
hs.setStyles (this.wrapper, {
position: on ? 'fixed' : 'absolute',
zoom: 1, // IE7 hasLayout bug,
left: (parseInt(stl.left) + sign * hs.page.scrollLeft) +'px',
top: (parseInt(stl.top) + sign * hs.page.scrollTop) +'px'
});
if (this.outline) {
stl = this.outline.table.style;
hs.setStyles (this.outline.table, {
position: on ? 'fixed' : 'absolute',
zoom: 1, // IE7 hasLayout bug,
left: (parseInt(stl.left) + sign * hs.page.scrollLeft) +'px',
top: (parseInt(stl.top) + sign * hs.page.scrollTop) +'px'
});
}
this.fixed = on; // flag for use on dragging
},
onAfterExpand: function() {
this.fix(true); // fix the popup to viewport coordinates
},
onBeforeClose: function() {
this.fix(false); // unfix to get the animation right
},
onDrop: function() {
this.fix(true); // fix it again after dragging
},
onDrag: function(sender, args) {
//if (this.fixed) { // only unfix it on the first drag event
this.fix(true);
//}
}
});
Related
I want to show 30 days in Day View Scheduler with Horizontal Scrollbar. Currently, Horizontal Scrollbar is available only for Timeline View but I want it for Day View as well as Month View.
For Timeline View with Horizontal Scrollbar code:
scheduler.createTimelineView({
name: "timeline",
x_unit: "minute",
x_date: "%H:%i",
x_step: 30,
x_size: 24*7,
x_start: 16,
x_length: 48,
y_unit: sections,
y_property: "section_id",
render: "bar",
scrollable: true,
column_width: 70,
scroll_position:new Date(2018, 0, 15) });
Please share your ideas and Sample links
Thanks in Advance
Try using Custom View. You can remove the default Day view and display your own instead, with the number of days you want to display. This can be done like this:
First in scheduler.config.header set tab "thirty_days" instead of "day":
scheduler.config.header = [
"thirty_days",
"week",
"month",
"date",
"prev",
"today",
"next"
];
The label for the view is set as in:
scheduler.locale.labels.thirty_days_tab = "Days";
Next, set the start date of the viewing interval, as well as viewing templates. It's better to create the custom view in the onTemplatesReady event handler function so that your custom view templates are ready before the scheduler is initialized:
scheduler.attachEvent("onTemplatesReady", () => {
scheduler.date.thirty_days_start = function(date) {
const ndate = new Date(date.valueOf());
ndate.setDate(Math.floor(date.getDate()/10)*10+1);
return this.date_part(ndate);
}
scheduler.date.add_thirty_days = function(date,inc) {
return scheduler.date.add(date,inc*30,"day");
}
const format = scheduler.date.date_to_str(scheduler.config.month_day);
scheduler.templates.thirty_days_date = scheduler.templates.week_date;
scheduler.templates.thirty_days_scale_date = function(date) {
return format(date);
};
});
To add horizontal scrolling to the view, you can place the scheduler inside the scrollable element and give the scheduler the width required to display all columns. You'll need to hide a default navigation panel of the scheduler and create a custom one with HTML, so it would have a correct width and won't be affected by scrolling:
scheduler.xy.nav_height = 0;
scheduler.attachEvent("onSchedulerReady", function () {
const navBar = scheduler.$container.querySelector(".dhx_cal_navline").cloneNode(true);
navBar.style.width = "100%";
document.querySelector(".custom-scheduler-header").appendChild(navBar);
document.querySelectorAll(".custom-scheduler-header .dhx_cal_tab").forEach(function (tab) {
tab.onclick = function () {
const name = tab.getAttribute("name");
const view = name.substr(0, name.length - 4);
scheduler.setCurrentView(null, view);
};
});
document.querySelector(".custom-scheduler-header .dhx_cal_prev_button").onclick = function () {
const state = scheduler.getState();
scheduler.setCurrentView(scheduler.date.add(state.date, -1, state.mode));
};
document.querySelector(".custom-scheduler-header .dhx_cal_next_button").onclick = function () {
const state = scheduler.getState();
scheduler.setCurrentView(scheduler.date.add(state.date, 1, state.mode));
};
document.querySelector(".custom-scheduler-header .dhx_cal_today_button").onclick = function () {
scheduler.setCurrentView(new Date());
};
scheduler.attachEvent("onBeforeViewChange", (oldView, oldDate, newView, newDate) => {
const innerContainer = document.getElementById("scheduler_here");
if (newView === "thirty_days") {
innerContainer.style.width = "3000px";
} else {
innerContainer.style.width = "100%";
}
return true;
});
scheduler.attachEvent("onViewChange", function (view, date) {
const dateLabel = document.querySelector(".custom-scheduler-header .dhx_cal_date");
const state = scheduler.getState();
dateLabel.innerHTML = scheduler.templates[view + "_date"](state.min_date, state.max_date);
document.querySelectorAll(".custom-scheduler-header .dhx_cal_tab").forEach(function(tab) {
tab.classList.remove("active");
});
const activeTab = document.querySelector(".custom-scheduler-header ." + view + "_tab");
if (activeTab) {
activeTab.classList.add("active");
}
});
});
Styles that you will need:
.custom-scheduler-header .dhx_cal_navline{
display: block !important;
height: 60px !important;
}
.custom-scheduler-header .dhx_cal_navline.dhx_cal_navline_flex{
display: flex !important;
}
.dhx-scheduler {
height: 100vh;
width: 100vw;
position: relative;
overflow: hidden;
background-color: #fff;
font-family: Roboto, Arial;
font-size: 14px;
}
.dhx_cal_container .dhx_cal_navline {
display: none;
}
Please see an example: https://snippet.dhtmlx.com/znd7ffiv
You may need to fix the hour scale so that it remains visible when scrolling horizontally on the calendar. I did not implement this in the example, I think that this can be done in the same way as for the navigation panel. If you need, write to me and I will send an update in a few working days.
As for the "Month" view, the approach is the same as for the "Day" view.
I am trying to make a 'Movie-Chart' with ChartJs v3.9.1 where I add and remove data:
https://codepen.io/ipax77/pen/YzLrNEM
<...>
let chartdata = { x: label, y: stepdata.Winrate };
chart.data.labels.push(label);
chart.data.datasets.forEach(dataset => {
if (dataset.label == stepdata.Commander) {
dataset.data.push(chartdata);
dataadded = true;
}
});
chart.update();
<...>
if (chart.data.labels.length > 6) {
let removelabel = chart.data.labels[0];
chart.data.labels.splice(0, 1);
chart.data.datasets.forEach(dataset => {
const index = dataset.data.findIndex(obj => obj.x == removelabel);
if (index > -1) {
dataset.data.splice(index, 1);
}
});
Somehow the animation for new datapoints starts from the xAxis rather than the previouse datapoint. Is there a way to fix this?
I tried working with timescale axis and chartjs-adapter-date-fns which fixes the problem, but then the animation for lines that don't start with the first label are messed up.
You could disable animation on the y-axis by defining options.animation as follows:
options: {
responsive: true,
animation: {
y: {
duration: 0
}
},
I have a list of text elements and want to automatically scroll my list to the bottom when I'm dragging my new element.
This example below works properly once I drag-and-dropped one time an element in a list.
I believe I need to call once an observable before the drag.
I'm using dragula and dom-autoscrolling.
import {takeUntil} from "rxjs/internal/operators/takeUntil";
import * as autoScroll from 'dom-autoscroller';
const drake = this.dragulaService.find(this.dragulaBagName);
this.dragulaService.drag.pipe(
takeUntil(this.destroyed$),
).subscribe(([bag, movingEl, containerEl]) => {
autoScroll(containerEl.parentNode, {
margin: 20,
pixels: 10,
scrollWhenOutside: true,
autoScroll: function () {
return this.down && drake && drake.drake && drake.drake.dragging;
}
});
});
Apparently, this.down in callback autoScroll is set to false at the beginning... once drag-and-dropped one time, it works correctly.
Any ideas?
try use (mousedown)="initAutoScroll()"
initAutoScroll(){
const drake = this.dragulaService.find(this.dragulaBagName);
this.scroll = autoScroll(
containerEl.parentNode,
{
margin: 30,
maxSpeed: 25,
scrollWhenOutside: true,
autoScroll: function () {
return this.down && drake.drake.dragging;
}
});
}
this.dragulaService.dragend.asObservable().subscribe(value => {
if (this.scroll) {
this.scroll.destroy(); // destroy when don't use auto-scroll
}
});
I am cloning a selected object on a canvas in Fabric.js using a simple function.
function duplicateObj() {
var obj = canvas.getActiveObject();
var clone = fabric.util.object.clone(obj);
clone.set({left: 100,top: 100});
canvas.add(clone);
}
That works absolutely fine. Now if I work with the object and the clone is not required anymore and I select and delete it, both objects, the clone and the original object are deleted. The delete function is:
function deleteObj() {
var obj = canvas.getActiveObject();
canvas.fxRemove(obj);
}
The objects are the same. Is there are way to clone objects and make the clone independent of the of the original? I tried this:
function duplicateObj() {
var obj = canvas.getActiveObject();
var clone = fabric.util.object.clone(obj);
clone.initialize();
$.extend(clone, obj);
fabric.util.object.extend(clone, obj);
clone.set({left: 100,top: 100});
canvas.add(clone);
}
It works, however the objects are the same again and if I only use initialize I am ending up with an object that has now properties set.
here is the solution
var object = fabric.util.object.clone(canvas.getActiveObject());
object.set("top", object.top+5);
object.set("left", object.left+5);
canvas.add(object);
This worked very well for me, and the cloned object is totally unlinked from the original:
var object = canvas.getActiveObject();
object.clone(function(clone) {
canvas.add(clone.set({
left: object.left + 10,
top: object.top + 10
}));
});
And you can do it to clone all selected objects:
var activeObjects = canvas.getActiveObjects();
if (activeObjects) {
activeObjects.forEach(function(object) {
object.clone(function(clone) {
canvas.add(clone.set({
left: object.left + 10,
top: object.top + 10
}));
})
});
}
I hope it can help you!
I was having a similar issue where actions on the clone would affect the original object. I opted to just serialize the object and deserialize it into a new object:
var copyData = canvas.getActiveObject().toObject();
fabric.util.enlivenObjects([copyData], function(objects) {
objects.forEach(function(o) {
o.set('top', o.top + 15);
o.set('left', o.left + 15);
canvas.add(o);
});
canvas.renderAll();
});
for fabricjs 2.0
$(".copy").on("click", function () {
var activeObject = canvas.getActiveObject();
activeObject.clone(function (cloned) {
canvas.discardActiveObject();
cloned.set({
top: cloned.top + 20,
evented: true
});
if (cloned.type === 'activeSelection') {
// active selection needs a reference to the canvas.
cloned.canvas = canvas;
cloned.forEachObject(function (obj) {
canvas.add(obj);
});
cloned.setCoords();
} else {
canvas.add(cloned);
}
canvas.setActiveObject(cloned);
canvas.requestRenderAll();
});
});
Here is my implementation of cloning selected object or group.
https://jsfiddle.net/milanhlinak/rxtjm7w0/1/
<!DOCTYPE html>
<html>
<head>
<script type="text/javascript" src="lib/jquery-3.1.1.min.js"></script>
<script type="text/javascript" src="lib/fabric.min.js"></script>
</head>
<body>
<button onclick="cloneSelected()">Clone selected</button>
<canvas id="canvas" style="border: 1px solid #cccccc"></canvas>
<script>
var canvas = new fabric.Canvas('canvas', {
width: 500,
height: 500,
});
canvas.add(new fabric.Rect({
left: 100,
top: 100,
width: 50,
height: 50,
fill: '#faa'
}));
canvas.add(new fabric.Circle({
left: 300,
top: 300,
radius: 25,
fill: '#afa'
}));
function cloneSelected() {
console.log('cloneSelected');
var activeObject = canvas.getActiveObject();
var activeGroup = canvas.getActiveGroup();
if (activeObject) {
console.log('object selected');
var clonedObject = null;
var json = activeObject.toJSON();
if (json.type == 'rect') {
clonedObject = new fabric.Rect(json);
} else if (json.type == 'circle') {
clonedObject = new fabric.Circle(json);
} else {
console.log('unknown object type: ' + json.type);
return;
}
var oldLeft = clonedObject.getLeft();
var oldTop = clonedObject.getTop();
clonedObject.setLeft(oldLeft + 10);
clonedObject.setTop(oldTop + 10);
var boundingRect = clonedObject.getBoundingRect(true);
if (boundingRect.left + boundingRect.width > canvas.getWidth()) {
clonedObject.setLeft(oldLeft);
}
if (boundingRect.top + boundingRect.height > canvas.getHeight()) {
clonedObject.setTop(oldTop);
}
canvas.add(clonedObject);
canvas.setActiveObject(clonedObject);
canvas.renderAll();
console.log('selected object cloned');
} else if (activeGroup) {
console.log('group selected');
canvas.discardActiveGroup();
var clonedObjects = [];
activeGroup.getObjects().forEach(function (object) {
var clonedObject = null;
var json = object.toJSON();
if (json.type == 'rect') {
clonedObject = new fabric.Rect(json);
} else if (json.type === 'circle') {
clonedObject = new fabric.Circle(json);
} else {
console.log('unknown object type: ' + json.type);
return;
}
clonedObject.setCoords();
canvas.add(clonedObject);
clonedObject.set('active', true);
clonedObjects.push(clonedObject);
});
var group = new fabric.Group(clonedObjects.reverse(), {
canvas: canvas
});
group.addWithUpdate(null);
var oldLeft = group.getLeft();
var oldTop = group.getTop();
group.setLeft(oldLeft + 10);
group.setTop(oldTop + 10);
var boundingRect = group.getBoundingRect(true);
if (boundingRect.left + boundingRect.width > canvas.getWidth()) {
group.setLeft(oldLeft);
}
if (boundingRect.top + boundingRect.height > canvas.getHeight()) {
group.setTop(oldTop);
}
group.setCoords();
canvas.setActiveGroup(group);
group.saveCoords();
canvas.renderAll();
console.log('selected objects cloned');
} else {
console.log('no object selected');
}
}
</script>
</body>
</html>
You can use
var obj = canvas.getActiveObject();
obj.clone(function(c) {
canvas.add(c.set({ left: 100, top: 100, angle: -15 }));
});
Here you can see it working:
http://fabricjs.com/opacity_mouse_move/
I wanted a clone to be able to reset an item to the originally saved state...like others I found objects pass on variables to the clone and vice versa...
What i did was create a var to hold the clone for later recall like this:
var o;
var object = canvas.getActiveObject().clone(
function(obj){
o = obj;
}
);
one caveat if you set id's and dont make them unique during cloning process it makes the whole thing go wild...unless you delete the original first..
Check the demo for Copy and Paste here:
http://fabricjs.com/copypaste
Here is the code to copy/paste or clone the selected object.
function Clone() {
Copy();
Paste()
}
function Copy() {
// clone what are you copying since you
// may want copy and paste on different moment.
// and you do not want the changes happened
// later to reflect on the copy.
canvas.getActiveObject().clone(function(cloned) {
_clipboard = cloned;
});
}
function Paste() {
// clone again, so you can do multiple copies.
_clipboard.clone(function(clonedObj) {
canvas.discardActiveObject();
clonedObj.set({
left: clonedObj.left + 10,
top: clonedObj.top + 10,
evented: true,
});
if (clonedObj.type === 'activeSelection') {
// active selection needs a reference to the canvas.
clonedObj.canvas = canvas;
clonedObj.forEachObject(function(obj) {
canvas.add(obj);
});
// this should solve the unselectability
clonedObj.setCoords();
} else {
canvas.add(clonedObj);
}
_clipboard.top += 10;
_clipboard.left += 10;
canvas.setActiveObject(clonedObj);
canvas.requestRenderAll();
});
}
So I got this jQuery plugin to active a sliding menu tab:
$(document).ready(function() {
var closeAll,
$parentItem = $('div#tabWrapper'),
slideAmt = $('div#tabBG').width(),
direction;
$('a#toggle').click(function() {
if (parseInt($parentItem.css('marginLeft'), 10) < 0) {
direction = '+=';
$(this).addClass('expanded');
}
else {
$(this).removeClass('expanded');
direction = '-=';
}
$parentItem.animate({marginLeft: direction + slideAmt}, 400)
return false;
});
$parentItem.mouseleave(function() {
closeAll = setTimeout(function() {
$('a#toggle').removeClass('expanded').parent().animate({marginLeft: -slideAmt}, 300);
return false;
}, 600);
})
.mouseenter(function() {
clearTimeout(closeAll);
}) });
But I only want this animation to work on a higher screen width than 1024px.
How would make this happen?
Thanks all!
You can get the screen resolution using
screen.height;
screen.width;
Then do what you want to do depending on these values