I am trying to build collaborative canvas with socket-io and with react-canvas-draw
, but when I take a low opacity on brush-color, it just draws multiple circles on other screen, as shown here :
when I draw on one screen slowly, it just over draws on same location or something with many many circles,
Drawing.js
import DrawableCanvas from "react-canvas-draw";
import { Button } from "reactstrap";
import {SocketContext} from './context/socket';
const { Component } = require("react");
var canvas, context;
/* VARIABLES */
var drawing = false;
// determines the current x or y position
var current = {x: 0, y: 0};
export class Drawing extends Component {
// this.context refers to socket.io making it available for all components
static contextType = SocketContext;
constructor(props) {
super(props);
this.state = {
canvasWidth: 1400,
canvasHeight: 1400,
catenaryColor: "rgba(0,0,0,0.68)",
brushColor: "rgba(0,0,0,0.68)",
brushRadius: 1,
lazyRadius: 0,
clear: false,
hideInterface: false,
onChange: null,
loadTimeOffset: 5,
backgroundColor: "#FFF",
hideGrid: false,
disabled: false,
saveData: "",
immediateLoading: true,
usingType: "draw",
};
}
componentDidMount() {
canvas = document.getElementsByName("canvas")[0].children[0].children[1];
context = canvas.getContext("2d");
this.context.on('drawing', data => this.onDrawingEvent(data))
}
onDrawingEvent(data) {
this.drawLine(
data.x0,
data.y0,
data.x1,
data.y1,
data.color,
data.brushRadius,
false,
true,
);
}
drawLine(x0, y0, x1, y1, color,brushRadius, emit,duplicate) {
// Gets the offset so it fits to any window size
var canvasTopPosition = document.getElementsByName("canvas")[0].offsetTop;
var canvasLeftPosition = document.getElementsByName("canvas")[0].offsetLeft;
// If Duplicate is false, it will draw on both monitors and cause duplication.
if(duplicate)
{
context.beginPath();
context.moveTo(x0, y0);
context.lineTo(x1, y1);
context.strokeStyle = color;
context.lineWidth = brushRadius * 2;
context.lineCap = 'round';
context.stroke();
context.closePath();
}
if (!emit) {
return;
}
this.context.emit('drawing', {
x0: x0 - canvasLeftPosition,
y0: y0 - canvasTopPosition,
x1: x1 - canvasLeftPosition,
y1: y1 - canvasTopPosition,
color: color,
brushRadius : this.state.brushRadius,
type: this.state.usingType,
globalCompositeOperation: context.globalCompositeOperation,
message_type: "draw"
});
}
onMouseDown = (e) => {
drawing = true;
current.x = e.clientX || e.touches[0].clientX;
current.y = e.clientY || e.touches[0].clientY;
};
onMouseUp = (e) => {
if (!drawing) {
return;
}
drawing = false;
this.drawLine(
current.x,
current.y,
e.clientX,
e.clientY,
this.state.brushColor,
this.state.brushRadius,
true,
false,
);
};
onMouseMove = (e) => {
if (!drawing) {
return;
}
this.drawLine(current.x, current.y, e.clientX || e.touches[0].clientX, e.clientY || e.touches[0].clientY,this.state.brushColor,this.state.brushRadius,true,false);
current.x = e.clientX || e.touches[0].clientX;
current.y = e.clientY || e.touches[0].clientY;
};
render() {
return (
<div
name="canvas"
onMouseDown={this.onMouseDown}
onMouseUp={this.onMouseUp}
onMouseMove={this.onMouseMove}
onMouseLeave={() => drawing = false} // Determines if the mouse is outside the div
>
<DrawableCanvas
/>
</div>
);
}
}
export default Drawing;
server.js
const express = require('express');
const http = require("http");
const port = process.env.PORT || 5000;
const cors = require("cors");
const app = express();
const server = http.createServer(app);
const socket = require("socket.io");
const io = socket(server, {
cors: true,
});
app.use(cors());
io.on('connection', onConnection);
function onConnection(socket){
socket.on('drawing', (data) => socket.broadcast.emit('drawing', data));
}
server.listen(port, () => console.log('server is running on port 5000'));
socket.js
import io from "socket.io-client";
import React from 'react';
const SOCKET_URL = "http://localhost:5000";
export const socket = io.connect(SOCKET_URL, { secure: true });
export const SocketContext = React.createContext(socket);
Related
I am developing a whiteboard application using react, node.js and socket.io. My requirement is when I open the whiteboard it will also visible to other people/user.
How can I achieve that? In current situation user have to click on a button to open the whiteboard from ther end. I am new to react and socket.io. It will be very helpful if anybody share any knowledge about the idea.
I have shared some code done so far.
server.js
const express = require('express');
const app = express();
const path = require('path');
const http = require('http').createServer(app);
const io = require('socket.io')(http);
io.on('connection', onConnection);
function onConnection(socket){
socket.on('drawing', (data) => {
socket.broadcast.emit('drawing',data);
console.log(data);
});
socket.on('clear', () => {
socket.broadcast.emit('clear');
console.log('data cleard........');
});
}
app.get('*', (_, res) => res.sendFile(path.join(__dirname, 'build/index.html')));
const server_port = 8081;
http.listen(server_port, () => {
console.log(`Started on : ${server_port}`);
})
canvas.jsx
const Canvas = () => {
const canvasRef = useRef(null);
const colorsRef = useRef(null);
const socketRef = useRef();
const classes = useStyles();
const [color, setColor] = useState('#3B3B3B');
const [size, setSize] = useState('3');
const [cursor, setCursor] = useState('default');
const [isDrawing, setIsDrawing] = useState(false);
const timeout = useRef(null);
const ctx = useRef(null);
useEffect(() => {
const canvas = canvasRef.current;
ctx.current = canvas.getContext('2d');
//Resizing
canvas.height = window.innerHeight;
canvas.width = window.innerWidth;
//Load from locastorage
const canvasimg = localStorage.getItem('canvasimg');
if (canvasimg) {
var image = new Image();
ctx.current = canvas.getContext('2d');
image.onload = function() {
ctx.current.drawImage(image, 0, 0);
setIsDrawing(false);
};
image.src = canvasimg;
}
}, [ctx]);
useEffect(() => {
// // --------------- getContext() method returns a drawing context on the canvas-----
const canvas = canvasRef.current;
// const context = canvas.getContext('2d');
let drawing = false;
// ctx.current = context;
// ------------------------------- create the drawing ----------------------------
const drawLine = (x0, y0, x1, y1, emit) => {
ctx.current.beginPath();
ctx.current.moveTo(x0, y0);
ctx.current.lineTo(x1, y1);
ctx.current.strokeStyle = color;
ctx.current.lineWidth = size;
ctx.current.stroke();
ctx.current.closePath();
if (!emit) {
return;
}
const w = canvas.width;
const h = canvas.height;
socketRef.current.emit('drawing', {
x0: x0 / w,
y0: y0 / h,
x1: x1 / w,
y1: y1 / h,
});
};
// // ---------------- mouse movement --------------------------------------
const onMouseDown = e => {
drawing = true;
ctx.current.x = e.clientX || e.touches[0].clientX;
ctx.current.y = e.clientY || e.touches[0].clientY;
};
const onMouseMove = e => {
if (!drawing) {
return;
}
drawLine(
ctx.current.x,
ctx.current.y,
e.clientX || e.touches[0].clientX,
e.clientY || e.touches[0].clientY,
true
);
ctx.current.x = e.clientX || e.touches[0].clientX;
ctx.current.y = e.clientY || e.touches[0].clientY;
};
const onMouseUp = e => {
if (!drawing) {
return;
}
drawing = false;
drawLine(
ctx.current.x,
ctx.current.y,
e.clientX || e.touches[0].clientX,
e.clientY || e.touches[0].clientY,
true
);
};
// ----------- limit the number of events per second -----------------------
const throttle = (callback, delay) => {
let previousCall = new Date().getTime();
return function() {
const time = new Date().getTime();
if (time - previousCall >= delay) {
previousCall = time;
callback.apply(null, arguments);
}
};
};
// -----------------add event listeners to our canvas ----------------------
canvas.addEventListener('mousedown', onMouseDown, false);
canvas.addEventListener('mouseup', onMouseUp, false);
canvas.addEventListener('mouseout', onMouseUp, false);
canvas.addEventListener('mousemove', throttle(onMouseMove, 10), false);
// Touch support for mobile devices
canvas.addEventListener('touchstart', onMouseDown, false);
canvas.addEventListener('touchend', onMouseUp, false);
canvas.addEventListener('touchcancel', onMouseUp, false);
canvas.addEventListener('touchmove', throttle(onMouseMove, 10), false);
// -------------- make the canvas fill its parent component -----------------
const onResize = () => {
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
};
window.addEventListener('resize', onResize, false);
onResize();
// ----------------------- socket.io connection ----------------------------
socketRef.current = io.connect('/');
const onDrawingEvent = data => {
const w = canvas.width;
const h = canvas.height;
drawLine(data.x0 * w, data.y0 * h, data.x1 * w, data.y1 * h);
};
socketRef.current.on('drawing', onDrawingEvent);
socketRef.current.on('clear', clearCanvas);
}, []);
const emitAndCanvas = () => {
socketRef.current.emit('clear');
clearCanvas();
};
const clearCanvas = () => {
// localStorage.removeItem('canvasimg');
const canvas = canvasRef.current;
const context = canvas.getContext('2d');
// ctx.current.fillStyle = 'white';
context.clearRect(0, 0, canvas.width, canvas.height);
// socketRef.current.emit('clear');
//Passing clear screen
};
const getPen = () => {
setCursor('default');
setSize('3');
setColor('#3B3B3B');
};
const eraseCanvas = () => {
setCursor('grab');
setSize('20');
setColor('#FFFFFF');
$('#circularcursor').show();
$(document).ready(function() {
$(document).on('mousemove', function(e) {
$('#circularcursor').css({
left: e.pageX,
top: e.pageY,
});
});
});
};
return (
<>
<p id="message">Initializing Sync...</p>
<div className="canvas-btn">
<button onClick={getPen} className="btn-width">
<Tooltip title="Pencil">
<CreateRoundedIcon />
</Tooltip>
</button>
<div className="btn-width">
<input type="color" value={color} onChange={e => setColor(e.target.value)} />
</div>
<div>
<Select value={size} onChange={e => setSize(e.target.value)}>
<MenuItem value={1}>1</MenuItem>
<MenuItem value={3}>3</MenuItem>
<MenuItem value={5}>5</MenuItem>
<MenuItem value={10}>10</MenuItem>
<MenuItem value={15}>15</MenuItem>
<MenuItem value={20}>20</MenuItem>
<MenuItem value={25}>25</MenuItem>
</Select>
</div>
<div>
<button onClick={emitAndCanvas} className="btn-width">
<Tooltip title="Clear All">
<ClearAllIcon />
</Tooltip>
</button>
</div>
<div>
<button onClick={eraseCanvas} className="btn-width">
<Tooltip title="Reset">
<DeleteForeverRoundedIcon />
</Tooltip>
</button>
</div>
</div>
<canvas
style={{ cursor: cursor }}
// onMouseDown={onMouseMove}
// onMouseUp={onMouseUp}
// onMouseMove={drawLine}
ref={canvasRef}
className={classes.canvascover}
/>
</>
);
};
export default Canvas;
I am using web viewer and want to rotate individual pages and update them in the database.
Right now I am able to rotate the whole pdf only.
I am following this doc https://www.pdftron.com/documentation/web/guides/manipulation/rotate/
but not able to understand much
export default function PdfTron(props: any): ReactElement {
const viewer = useRef<HTMLDivElement>(null);
const {DrawingLibDetailsState, DrawingLibDetailsDispatch}: any = useContext(DrawingLibDetailsContext);
const [newInstance, setNewInstance] = useState<any>(null);
const [currentPage, setCurrentPage] = useState<any>(null);
const {dispatch, state }:any = useContext(stateContext);
//console.log("currentPage in state",currentPage)
useEffect(() => {
WebViewer(
{
path: '/webviewer/lib',
licenseKey: process.env["REACT_APP_PDFTRON_LICENSE_KEY"],
initialDoc: '',
filename: 'drawings',
extension: "pdf",
isReadOnly: true,
fullAPI: true,
disabledElements: [
// 'leftPanelButton',
// // 'selectToolButton',
// 'stickyToolButton',
// 'toggleNotesButton',
]
},
viewer.current as HTMLDivElement,
).then((instance: any) => {
setNewInstance(instance)
// you can now call WebViewer APIs here...
});
}, []);
useEffect(() => {
if(DrawingLibDetailsState?.parsedFileUrl?.url && newInstance ){
const s3Key = DrawingLibDetailsState?.parsedFileUrl?.s3Key;
const pageNum = s3Key.split('/')[s3Key.split('/').length-1].split('.')[0];
const fileName = DrawingLibDetailsState?.drawingLibDetails[0]?.fileName?.replace(".pdf", "");
const downloadingFileName = `page${pageNum}_${fileName}`;
newInstance.loadDocument(DrawingLibDetailsState?.parsedFileUrl?.url, {extension: "pdf",
filename: downloadingFileName ? downloadingFileName : 'drawing',})
const { documentViewer } = newInstance.Core;
const pageRotation = newInstance.Core.PageRotation;
const clickDocument =newInstance.Core.DocumentViewer.Click;
const pageNumber = newInstance.Core.pageNum;
//get page rotation from the PDF
documentViewer.addEventListener('rotationUpdated', (rotation: number) => {
updateRotation(rotation)
})
// trigger an event after the document loaded
documentViewer.addEventListener('documentLoaded', async() => {
const doc = documentViewer.getDocument();
const rotation = DrawingLibDetailsState?.drawingLibDetails[0]?.sheetsReviewed?.pdfRotation ?
DrawingLibDetailsState?.drawingLibDetails[0]?.sheetsReviewed?.pdfRotation : 0
documentViewer.setRotation(rotation)
})
documentViewer.on('pageNumberUpdated', () => {
DrawingLibDetailsDispatch(setDrawingPageNumber(0));
})
}
}, [DrawingLibDetailsState?.parsedFileUrl?.url, newInstance]);
useEffect(() => {
if(DrawingLibDetailsState?.drawingPageNum && newInstance ){
const { documentViewer, PDFNet } = newInstance.Core;
PDFNet.initialize()
documentViewer.addEventListener('documentLoaded',async () => {
await PDFNet.initialize()
const pdfDoc = documentViewer.getDocument();
const doc = await pdfDoc.getPDFDoc();
newInstance.UI.pageManipulationOverlay.add([
{
type: 'customPageOperation',
header: 'Custom options',
dataElement: 'customPageOperations',
operations: [
{
title: 'Alert me',
img: '/path-to-image',
onClick: (selectedPageNumbers:any) => {
alert(`Selected thumbnail pages: ${selectedPageNumbers}`);
},
dataElement: 'customPageOperationButton',
},
],
},
{ type: 'divider' },
]);
documentViewer.setCurrentPage(DrawingLibDetailsState?.drawingPageNum, true);
});
documentViewer.setCurrentPage(DrawingLibDetailsState?.drawingPageNum, true);
}
}, [DrawingLibDetailsState?.drawingPageNum]);
useEffect(() => {
if(props?.drawingSheetsDetails?.fileSize){
fetchSheetUrl(props?.drawingSheetsDetails)
}
}, [props?.drawingSheetsDetails]);
const fetchSheetUrl = (file: any) => {
const payload = [{
fileName: file.fileName,
key: file.sourceKey,
expiresIn: 100000000,
// processed: true
}];
getSheetUrl(payload);
}
const getSheetUrl = async (payload: any) => {
try {
dispatch(setIsLoading(true));
const fileUploadResponse = await postApi('V1/S3/downloadLink', payload);
if(fileUploadResponse.success){
const fileData = {
s3Key: payload[0].key,
url: fileUploadResponse.success[0].url
}
DrawingLibDetailsDispatch(setParsedFileUrl(fileData));
}
dispatch(setIsLoading(false));
} catch (error) {
Notification.sendNotification(error, AlertTypes.warn);
dispatch(setIsLoading(false));
}
}
const updateRotation = (rotation: number) => {
props.updateRotation(rotation)
}
return (
<>
<div className="webviewer" ref={viewer}></div>
</>
)
}
In WebViewer 8.0 you would need to enable the left panel by default when the document is loaded, and then use event delegation on left panel to watch for button clicks on the single page rotation buttons.
const { documentViewer } = instance.Core
documentViewer.addEventListener('documentLoaded',()=>{
let panelElement = instance.docViewer.getScrollViewElement().closest('#app').querySelector('[data-element="thumbnailsPanel"]');
if (!parentElement) {
instance.UI.toggleElementVisibility('leftPanel');
panelElement = instance.docViewer.getScrollViewElement().closest('#app').querySelector('[data-element="thumbnailsPanel"]');
}
panelElement.addEventListener('click', (e) => {
if (e.target.dataset?.element === 'thumbRotateClockwise' || e.target.dataset?.element === 'thumbRotateCounterClockwise') {
// The single page rotations are performed asychronously and there are no events firings in 8.0, so we have to manually add a delay before the page finishes rotating itself.
setTimeout(() => {
const pageNumber = parseInt(e.target.parentElement.previousSibling.textContent);
const rotation = instance.docViewer.getDocument().getPageRotation(pageNumber);
console.log('page ', pageNumber, ' self rotation is ', rotation);
}, 500);
}
});
})
If you have the option to upgrade to the latest WebViewer, you can listen to the ‘pagesUpdated’ event on documentViewer and the code becomes shorter & cleaner:
const { documentViewer } = instance.Core
documentViewer.addEventListener('pagesUpdated',(changes)=>{
changes.contentChanged.forEach(pageNumber=>{
const rotation = documentViewer.getDocument().getPageRotation(pageNumber)
console.log('page ', pageNumber, ' self rotation is ', rotation);
})
})
For both situations, when you load the document back, you can use documentViewer.getDocument().rotatePages to rotate to your saved rotations.
assuming we have the saved page rotations data structured like this
const rotationData = [
{pageNumber: 1, rotation: 180},
{pageNumber: 3, rotation: 90},
{pageNumber: 4, rotation: 270},
]
We can use the following code to rotate our individual pages back:
const { documentViewer } = instance.Core
documentViewer.addEventListener('documentLoaded',()=>{
rotationData.forEach(page=>{
const originalRotation = documentViewer.getDocument().getPageRotation(page.pageNumber)
if (originalRotation !== page.rotation) {
documentViewer.getDocument().rotatePages([page.pageNumber], (page.rotation-originalRotation)/90);
}
})
})
I'm trying to generate more than one thousand pdf files using surveyPDF.
The problem is that i can generate only 80 pdf files...
I'm passing an array with more than 1000 pdf to generate.
Code :
query.map(async items => {
const { generatePdf } = await import("~/lib/survey");
const filename = kebabCase(
`${campaign.title} - ${items.employee.fullName.toLowerCase()} -${moment().format("DD/MM/YYYY - HH:mm")} `
);
return generatePdf(campaign.template.body, items, filename, 210, 297);
});
The code which generate each pdfs :
import autoTable from "jspdf-autotable";
import { SurveyPDF, CommentBrick, CompositeBrick, PdfBrick, TextBrick } from "survey-pdf";
import { format } from "~/utils/date";
class AutoTableBrick extends PdfBrick {
constructor(question, controller, rect, options) {
super(question, controller, rect);
this.margins = {
top: controller.margins.top,
bottom: controller.margins.bot,
right: 30,
left: 30,
};
this.options = options;
}
renderInteractive() {
if (this.controller.doc.lastAutoTable && !this.options.isFirstQuestion) {
this.options.startY = this.yTop;
}
autoTable(this.controller.doc, {
head: [
[
{
content: this.question.title,
colSpan: 2,
styles: { fillColor: "#5b9bd5" },
},
],
],
margin: { ...this.margins },
styles: { fillColor: "#fff", lineWidth: 1, lineColor: "#5b9bd5", minCellWidth: 190 },
alternateRowStyles: { fillColor: "#bdd6ee" },
...this.options,
});
}
}
export async function generatePdf(json, data, filename, pdfWidth, pdfHeight) {
if (!json) {
return Promise.reject("Invalid json for pdf export");
}
for (const page of json.pages) {
page.readOnly = true;
}
const surveyPDF = new SurveyPDF(json, {
fontSize: 11,
format: [pdfWidth, pdfHeight],
commercial: true,
textFieldRenderAs: "multiLine",
});
surveyPDF.showQuestionNumbers = "off";
surveyPDF.storeOthersAsComment = false;
//TODO This does not work well with dynamic dropdown, bug declared
// surveyPDF.mode = "display";
surveyPDF.mergeData({ ...data, _: {} });
surveyPDF.onRenderQuestion.add(function(survey, options) {
const { bricks, question } = options;
if (question.getType() === "comment" && question.value && bricks.length > 0) {
for (const brick of bricks) {
if (brick.value) {
brick.value = question.value.replace(/\t/g, " ");
}
if (brick instanceof CompositeBrick) {
const { bricks } = brick;
for (const brick of bricks) {
if (brick instanceof CommentBrick) {
brick.value = question.value.replace(/\t/g, " ");
}
}
}
}
}
});
surveyPDF.onRenderQuestion.add(async function(survey, options) {
const {
point,
bricks,
question,
controller,
module: { SurveyHelper },
} = options;
if (question.getType() === "multipletext") {
const body = [];
let extraRows = 0;
let rows = question.getRows();
for (let i = 0; i < rows.length; i++) {
for (let j = 0; j < rows[i].length; j++) {
let { title, value, inputType } = rows[i][j];
if (inputType === "date") {
value = format(value);
}
if (typeof value === "string" && value.length > 0) {
const valueEstRows = value.match(/.{1,70}/g).length;
if (valueEstRows > 1) {
extraRows += valueEstRows;
}
}
body.push([title, value || "N/A"]);
}
}
//TODO Use SurveyHelper helperDoc do calculate the height of the auto table
const startY = point.yTop;
const height = 21.5 * (body.length + 1) + 8.5 * extraRows;
const isFirstQuestion = question.title === question.parent.questions[0].title;
options.bricks = [
new AutoTableBrick(question, controller, SurveyHelper.createRect(point, bricks[0].width, height), {
startY,
body,
isFirstQuestion,
}),
];
}
});
surveyPDF.onRenderQuestion.add(async function(survey, options) {
const {
point,
question,
controller,
module: { SurveyHelper },
} = options;
if (question.getType() === "text") {
//Draw question background
const { default: backImage } = await import("~/public/assets/images/block.png");
const backWidth = SurveyHelper.getPageAvailableWidth(controller);
const backHeight = SurveyHelper.pxToPt(100);
const imageBackBrick = SurveyHelper.createImageFlat(point, null, controller, backImage, backWidth, backHeight);
options.bricks = [imageBackBrick];
point.xLeft += controller.unitWidth;
point.yTop += controller.unitHeight;
const oldFontSize = controller.fontSize;
const titleBrick = await SurveyHelper.createTitleFlat(point, question, controller);
controller.fontSize = oldFontSize;
titleBrick.unfold()[0]["textColor"] = "#6a6772";
options.bricks.push(titleBrick);
//Draw text question text field border
let { default: textFieldImage } = await import("~/public/assets/images/input.png");
let textFieldPoint = SurveyHelper.createPoint(imageBackBrick);
textFieldPoint.xLeft += controller.unitWidth;
textFieldPoint.yTop -= controller.unitHeight * 3.3;
let textFieldWidth = imageBackBrick.width - controller.unitWidth * 2;
let textFieldHeight = controller.unitHeight * 2;
let imageTextFieldBrick = SurveyHelper.createImageFlat(
textFieldPoint,
null,
controller,
textFieldImage,
textFieldWidth,
textFieldHeight
);
options.bricks.push(imageTextFieldBrick);
textFieldPoint.xLeft += controller.unitWidth / 2;
textFieldPoint.yTop += controller.unitHeight / 2;
let textFieldValue = question.value || "";
if (textFieldValue.length > 90) {
textFieldValue = textFieldValue.substring(0, 95) + "...";
}
const textFieldBrick = await SurveyHelper.createBoldTextFlat(
textFieldPoint,
question,
controller,
textFieldValue
);
controller.fontSize = oldFontSize;
textFieldBrick.unfold()[0]["textColor"] = "#EFF8FF";
options.bricks.push(textFieldBrick);
}
});
surveyPDF.onRenderQuestion.add(async function(survey, options) {
const {
point,
question,
controller,
module: { SurveyHelper },
} = options;
if (question.getType() === "radiogroup" || question.getType() === "rating") {
options.bricks = [];
const oldFontSize = controller.fontSize;
const titleLocation = question.hasTitle ? question.getTitleLocation() : "hidden";
let fieldPoint;
if (["hidden", "matrix"].includes(titleLocation)) {
fieldPoint = SurveyHelper.clone(point);
} else {
const titleBrick = await SurveyHelper.createTitleFlat(point, question, controller);
titleBrick.xLeft += controller.unitWidth;
titleBrick.yTop += controller.unitHeight * 2;
controller.fontSize = oldFontSize;
titleBrick.unfold()[0]["textColor"] = "#6a6772";
options.bricks.push(titleBrick);
fieldPoint = SurveyHelper.createPoint(titleBrick);
fieldPoint.yTop += controller.unitHeight * 1.3;
}
//Draw checkbox question items field
const { default: itemEmptyImage } = await import("~/public/assets/images/unchecked.png");
const { default: itemFilledImage } = await import("~/public/assets/images/checked.png");
const itemSide = controller.unitWidth;
let imageItemBrick;
const choices = question.getType() === "rating" ? question.visibleRateValues : question.visibleChoices;
for (const choice of choices) {
const isItemSelected =
question.getType() === "rating" ? question.value === choice.value : choice === question.selectedItem;
imageItemBrick = SurveyHelper.createImageFlat(
fieldPoint,
null,
controller,
isItemSelected ? itemFilledImage : itemEmptyImage,
itemSide,
itemSide
);
options.bricks.push(imageItemBrick);
const textPoint = SurveyHelper.clone(fieldPoint);
textPoint.xLeft += itemSide + controller.unitWidth / 2;
textPoint.yTop += itemSide / 12;
const itemValue = choice.locText.renderedHtml;
const checkboxTextBrick = await SurveyHelper.createTextFlat(
textPoint,
question,
controller,
itemValue,
TextBrick
);
checkboxTextBrick.unfold()[0]["textColor"] = "#6a6772";
fieldPoint.yTop = imageItemBrick.yBot + SurveyHelper.GAP_BETWEEN_ROWS * controller.unitHeight;
options.bricks.push(checkboxTextBrick);
}
}
});
surveyPDF.onRenderFooter.add(function(survey, canvas) {
canvas.drawText({
text: canvas.pageNumber + "/" + canvas.countPages,
fontSize: 10,
horizontalAlign: "right",
margins: {
right: 12,
},
});
});
return await surveyPDF.raw(`./pdf/${filename}.pdf`);
}
The error :
FATAL ERROR: Ineffective mark-compacts near heap limit Allocation failed - JavaScript heap out of memory
I have already try to increase the node memory using $env:NODE_OPTIONS="--max-old-space-size=8192"
I am trying to make a group video call app with the help of a youtube tutorial. I am using react and socket io,
But somehow its giving me the above error. Can't understand what exactly I am doing wrong.
Kindly correct me.
Attaching the code for reference:
Create room page- The homepage
import React from "react";
import { v1 as uuid } from "uuid";
import { useNavigate } from "react-router-dom";
const CreateRoom = (props) => {
const navigate = useNavigate();
function create() {
const id = uuid();
//props.history.push(`/room/${id}`);
navigate(`/room/${id}`);
}
return (
<button onClick={create}>Create room</button>
);
};
export default CreateRoom;
Room.js - This is the landing page once call gets started.
import React, { useEffect, useRef, useState } from "react";
import io from "socket.io-client";
import Peer from "simple-peer";
import styled from "styled-components";
import { useParams } from "react-router-dom";
const Container = styled.div`
padding: 20px;
display: flex;
height: 100vh;
width: 90%;
margin: auto;
flex-wrap: wrap;
`;
const StyledVideo = styled.video`
height: 40%;
width: 50%;
`;
const Video = (props) => {
const ref = useRef();
useEffect(() => {
props.peer.on("stream", (stream) => {
ref.current.srcObject = stream;
});
});
return <StyledVideo playsInline autoPlay ref={ref} />;
};
const videoConstraints = {
height: window.innerHeight / 2,
width: window.innerWidth / 2,
};
const Room = (props) => {
const [peers, setPeers] = useState([]);
const socketRef = useRef();
const userVideo = useRef();
const peersRef = useRef([]);
let {roomID} = useParams();
console.log(roomID);
useEffect(() => {
socketRef.current = io.connect("http://localhost:8000");
navigator.mediaDevices
.getUserMedia({ video: videoConstraints, audio: true })
.then((stream) => {
userVideo.current.srcObject = stream;
socketRef.current.emit("join room", roomID);
socketRef.current.on("all users", (users) => {
const peers = [];
users.forEach((userID) => {
const peer = createPeer(userID, socketRef.current.id, stream);
peersRef.current.push({
peerID: userID,
peer,
});
peers.push(peer);
});
setPeers(peers);
});
socketRef.current.on("user joined", (payload) => {
const peer = addPeer(payload.signal, payload.callerID, stream);
peersRef.current.push({
peerID: payload.callerID,
peer,
});
setPeers((users) => [...users, peer]);
});
socketRef.current.on("receiving returned signal", (payload) => {
const item = peersRef.current.find((p) => p.peerID === payload.id);
item.peer.signal(payload.signal);
});
});
});
function createPeer(userToSignal, callerID, stream) {
const peer = new Peer({
initiator: true,
trickle: false,
stream,
});
peer.on("signal", (signal) => {
socketRef.current.emit("sending signal", {
userToSignal,
callerID,
signal,
});
});
return peer;
}
function addPeer(incomingSignal, callerID, stream) {
const peer = new Peer({
initiator: false,
trickle: false,
stream,
});
peer.on("signal", (signal) => {
socketRef.current.emit("returning signal", { signal, callerID });
});
peer.signal(incomingSignal);
return peer;
}
return (
<Container>
<StyledVideo muted ref={userVideo} autoPlay playsInline />
{peers.map((peer, index) => {
return <Video key={index} peer={peer} />;
})}
</Container>
);
};
export default Room;
Now for server end, this is my server code:
require('dotenv').config();
const express = require("express");
const http = require("http");
const app = express();
const cors = require("cors");
const server = http.createServer(app);
const socket = require("socket.io");
const io = socket(server);
const users = {};
const socketToRoom = {};
app.use(cors);
io.on('connection', socket => {
socket.on("join room", roomID => {
if (users[roomID]) {
const length = users[roomID].length;
if (length === 4) {
socket.emit("room full");
return;
}
users[roomID].push(socket.id);
} else {
users[roomID] = [socket.id];
}
socketToRoom[socket.id] = roomID;
const usersInThisRoom = users[roomID].filter(id => id !== socket.id);
socket.emit("all users", usersInThisRoom);
});
socket.on("sending signal", payload => {
io.to(payload.userToSignal).emit('user joined', { signal: payload.signal, callerID: payload.callerID });
});
socket.on("returning signal", payload => {
io.to(payload.callerID).emit('receiving returned signal', { signal: payload.signal, id: socket.id });
});
socket.on('disconnect', () => {
const roomID = socketToRoom[socket.id];
let room = users[roomID];
if (room) {
room = room.filter(id => id !== socket.id);
users[roomID] = room;
}
});
});
server.listen(process.env.PORT || 8000, () => console.log('server is running on port 8000'));
This is the link for youtube tutorial:
https://www.youtube.com/watch?v=R1sfHPwEH7A&t=154s
i am new in react native,i try to developing native application in react native but i have onPress click event issue.
InfoSwipper.js file i was click on Sign In button display "undefined is not an object(evaluating 'this.props.navigator.push') ". I am not sure what I am missing. please some one help me.
Thank you in advance.
index.android.js file code
"use strict";
import React, { Component } from 'react';
import {
AppRegistry,
StyleSheet,
Text,
View,
Navigator,
StatusBar,
AsyncStorage,
} from 'react-native';
var SignUp = require("./components/SignUp");
var InfoScreens = require("./components/InfoScreens");
import SplashScreen from 'react-native-splash-screen'
var Kahaani = React.createClass({
_renderScene(route, navigator) {
if (route.id === 0) {
return <InfoScreens navigator={navigator} />
}
else if (route.id === 1) {
StatusBar.setHidden(false);
console.log('WRONG SIGNIN', "here");
return <LogIn navigator={navigator} />
}
},
componentDidMount() {
// do anything while splash screen keeps, use await to wait for an async task.
SplashScreen.hide();
},
render: function() {
return (
<Navigator
style={styles.navigationContainer}
initialRoute={{id: 0, }}
renderScene={this._renderScene} />
);
}
});
var styles = StyleSheet.create({
navigationContainer: {
flex: 1,
},
});
AppRegistry.registerComponent('Kahaani', () => Kahaani);
swipperScreen.js
'use strict';
import React, { Component } from 'react';
import {
Dimensions, // Detects screen dimensions
Platform, // Detects platform running the app
ScrollView, // Handles navigation between screens
StyleSheet, // CSS-like styles
View,
Text,Navigator, TouchableHighlight, // Container component
} from 'react-native';
import LogIn from './LogIn';
var navigator;
class InfoScreenSwiper extends Component {
constructor(props)
{
super(props);
}
static defaultProps = {
horizontal: true,
pagingEnabled: true,
showsHorizontalScrollIndicator: false,
showsVerticalScrollIndicator: false,
bounces: false,
scrollsToTop: false,
removeClippedSubviews: true,
automaticallyAdjustContentInsets: false,
index: 0
};
state = this.initState(this.props);
initState(props) {
const total = props.children ? props.children.length || 1 : 0,
index = total > 1 ? Math.min(props.index, total - 1) : 0,
offset = width * index;
const state = {
total,
index,
offset,
width,
height,
};
this.internals = {
isScrolling: false,
offset
};
return state;
}
onScrollBegin = e => {
// Update internal isScrolling state
this.internals.isScrolling = true;
}
onScrollEnd = e => {
// Update internal isScrolling state
this.internals.isScrolling = false;
// Update index
this.updateIndex(e.nativeEvent.contentOffset
? e.nativeEvent.contentOffset.x
// When scrolled with .scrollTo() on Android there is no contentOffset
: e.nativeEvent.position * this.state.width
);
}
onScrollEndDrag = e => {
const { contentOffset: { x: newOffset } } = e.nativeEvent,
{ children } = this.props,
{ index } = this.state,
{ offset } = this.internals;
if (offset === newOffset &&
(index === 0 || index === children.length - 1)) {
this.internals.isScrolling = false;
}
}
updateIndex = (offset) => {
const state = this.state,
diff = offset - this.internals.offset,
step = state.width;
let index = state.index;
if (!diff) {
return;
}
index = parseInt(index + Math.round(diff / step), 10);
this.internals.offset = offset;
this.setState({
index
});
}
swipe = () => {
if (this.internals.isScrolling || this.state.total < 2) {
return;
}
const state = this.state,
diff = this.state.index + 1,
x = diff * state.width,
y = 0;
this.scrollView && this.scrollView.scrollTo({ x, y, animated: true });
this.internals.isScrolling = true;
if (Platform.OS === 'android') {
setImmediate(() => {
this.onScrollEnd({
nativeEvent: {
position: diff
}
});
});
}
}
renderScrollView = pages => {
return (
<ScrollView ref={component => { this.scrollView = component; }}
{...this.props}
contentContainerStyle={[styles.wrapper, this.props.style]}
onScrollBeginDrag={this.onScrollBegin}
onMomentumScrollEnd={this.onScrollEnd}
onScrollEndDrag={this.onScrollEndDrag}
>
{pages.map((page, i) =>
// Render each slide inside a View
<View style={[styles.fullScreen, styles.slide]} key={i}>
{page}
</View>
)}
</ScrollView>
);
}
renderPagination = () => {
if (this.state.total <= 1) {
return null;
}
const ActiveDot = <View style={[styles.dot, styles.activeDot]} />,
Dot = <View style={styles.dot} />;
let dots = [];
for (let key = 0; key < this.state.total; key++) {
dots.push(key === this.state.index
// Active dot
? React.cloneElement(ActiveDot, { key })
// Other dots
: React.cloneElement(Dot, { key })
);
}
return (
<View
pointerEvents="none"
style={[styles.pagination, styles.fullScreen]}>
{dots}
</View>
);
}
onMainPressLogIn= () =>
{
console.log('WRONG SIGNIN', "onMainPressLogIn");
this.props.navigator.push({
id:1
});
}
onSubmitPressed () {
console.log('WRONG SIGNIN', "onSubmitPressed");
this.props.navigator.push({
id:2
})
}
renderButtonBottom = () => {
const lastScreen = this.state.index === this.state.total - 1;
return (
<View pointerEvents="box-none" style={[styles.buttonWrapper]}>
<View style={ [styles.viewButton]}>
<Text style={[styles.buttonLogIn,styles.buttonAll]} onPress={this.onMainPressLogIn} >LOG IN</Text>
<Text style={[styles.buttonSignUp,styles.buttonAll]} onPress={(this.onSubmitPressed.bind(this))}>SIGN UP</Text>
</View>
</View>
);
}
render = ({ children } = this.props) => {
return (
<View style={[styles.container, styles.fullScreen]}>
{/* Render screens */}
{this.renderScrollView(children)}
{/* Render pagination */}
{this.renderPagination()}
{/* Render Continue or Done button */}
{this.renderButtonBottom()}
</View>
);
}
}
module.exports = InfoScreenSwiper;