我正在使用ElectronJS创建一个可从灰度图像生成STL文件的应用程序。在这种情况下,我有一些嵌套的循环,它们遍历每个其他像素以将每个构面的坐标写入文件。我正在尝试通过更新div的width属性来为该过程实现进度条(处理4k图像最多需要30分钟)。但是,即使我在循环中设置了新的宽度,似乎宽度也要等到循环完成后才更新。我更新样式的速度太快了吗?
由于我没有在下面包括其余的js,因此“选择文件”按钮使用户可以从其文件系统中选择一个文件(即图像)。转换按钮使用该文件路径和Opencv来查找图像像素的“高度”(读取为灰度),并将连接这些“高度”所产生的构面写入stl文件“。
这是我的HTML:
<!DOCTYPE html>
<html>
<head>
<title>File Select</title>
<link rel="stylesheet" href="../css/convert.css"/>
</head>
<body>
<button id="select-file-button" onclick="selectFile()">Select File</button>
<p id="filename"></p>
<div id="progress-bar" style="--width: 2%"></div>
<button id="test" onclick="convert()">Convert</button>
<!-- Excluded from page -->
<img id="img-for-analysis" src=""/>
<!-- Scripts -->
<script type="text/javascript" src="../js/convert.js"></script>
</body>
</html>
这是CSS:
@font-face {font-family: "Orkney Light"; src: url('./fonts/orkney/Orkney\ Light.ttf')}
@font-face {font-family: "Orkney Medium"; src: url('./fonts/orkney/Orkney\ Medium.ttf')}
@font-face {font-family: "Orkney Bold"; src: url('./fonts/orkney/Orkney\ Bold.ttf')}
body {
margin: 0;
height: 100%;
overflow: hidden;
}
#progress-bar {
width: 50vw;
height: 5vh;
background-color: black;
border-radius: 5vw;
position: relative
}
#progress-bar::before {
content: "";
width: calc(var(--width, 0) * 1%);
position: absolute;
left: 1vw;
top: 1vh;
bottom: 1vh;
min-width: 0.1vw;
max-width: calc(100% - 2vw);
background-color: red;
border-radius: 10000vw;
}
#img-for-analysis {
margin-top: 100vh;
opacity: 0;
position: fixed;
}
这是链接的js文件中进行转换过程的部分,包括进度条的代码:
/*
* Progress bar
*/
const progressBar = document.getElementById('progress-bar')
/**
* Returns the current width of the progress bar
*/
function getProgressBarWidth() {
const computedStyle = getComputedStyle(progressBar)
const width = parseFloat(computedStyle.getPropertyValue('--width')) || 0
return width
}
/**
* Update the progress bar with the given value
* @param {number} newWidth - A positive number meant to be the new percent of the progress bar
*/
function updateProgressBar (newWidth) {
progressBar.style.setProperty('--width', newWidth * 100)
}
/*
* Convert
*/
/**
* Writes the generic beginning of a facet to the given stream
* @param {fs write stream} stream
*/
function writeFacetBeginning (stream) {
stream.write("facet normal 0 0 0" + "\n" + "outer loop" + "\n")
}
/**
* Writes the generic beginning of a caet to the given stream
* @param {fs write stream} stream
*/
function writeFacetEnd (stream) {
stream.write("endloop" + "\n" + "endfacet" + "\n")
}
/**
* Coverts the already selected file to an stl file
*/
function convert() {
// Not converting if there is no file selected
if(filePath == null) {
return
}
// Making the mat
let src = cv.imread('img-for-analysis', cv.IMREAD_GRAYSCALE)
// Variable to hold the pixel values of the image
let pixelValues = [...Array(src.rows)].map(e => Array(src.cols).fill(null))
//Storing the values of the pixels in the array
for(let i = 0; i < src.rows; i++) {
for (let j = 0; j < src.cols; j++) {
pixelValues[i][j] = parseInt(src.ucharAt(i, j * src.channels()))
}
}
// Creating the read stream
let stream = fs.createWriteStream(fileName.split('.')[0] + '.stl', {flags: 'w'})
// Settings
let scale = 1/255
// Status
let maxProgress = (src.rows * src.cols) // Every pixel + initial triangles for sides
let currentProgress = 0
stream.write("solid pic" + "\n")
/*
* Iterating through every other pixel to generate the facets of the top face
*
* * -- * -- *
* | \ | / |
* * -- * -- *
* | / | \ |
* * -- * -- *
*
*/
for (let row = 1; row < src.rows; row+=2)
{
for (let col = 1; col < src.cols; col+=2)
{
/*
*
* Creating facets w/ clockwise vertexes
*
* facet normal n1 n2 n3
* outer loop
* vertex x y z
* vertex x y z
* vertex x y z
* "\n"oop
* endfacets
*/
/* Top-Left
*--*
\ |
*
*/
writeFacetBeginning(stream);
stream.write("vertex " + row + " " + col + " " + pixelValues[row][col] * scale + "\n")
stream.write("vertex " + (row-1) + " " + col + " " + pixelValues[(row-1)][col] * scale + "\n")
stream.write("vertex " + (row-1) + " " +(col-1)+ " " + pixelValues[row-1][col-1] * scale + "\n")
writeFacetEnd(stream);
/* Top-Left
*
| \
*--*
*/
writeFacetBeginning(stream);
stream.write("vertex " + row + " " + col + " " + pixelValues[row][col] * scale + "\n")
stream.write("vertex " + (row-1) + " " +(col-1)+ " " + pixelValues[(row-1)][col - 1] * scale + "\n")
stream.write("vertex " + row + " " +(col-1)+ " " + pixelValues[row][col-1] * scale + "\n")
writeFacetEnd(stream);
/* Top-Right
*--*
| /
*
*/
if (col+1 < src.cols) //Is this pixel not on the last column
{
writeFacetBeginning(stream);
stream.write("vertex " + row + " " + col + " " + pixelValues[row][col] * scale + "\n")
stream.write("vertex " + (row-1) + " " +(col+1)+ " " + pixelValues[(row-1)][col + 1] * scale + "\n")
stream.write("vertex " + (row-1) + " " + col + " " + pixelValues[(row-1)][col] * scale + "\n")
writeFacetEnd(stream);
/* Top-Right
*
/ |
*--*
*/
writeFacetBeginning(stream);
stream.write("vertex " + row + " " + col + " " + pixelValues[row][col] * scale + "\n")
stream.write("vertex " + row + " " +(col+1)+ " " + pixelValues[row][col + 1] * scale + "\n")
stream.write("vertex " + (row-1) + " " +(col+1)+ " " + pixelValues[(row-1)][col + 1] * scale + "\n")
writeFacetEnd(stream);
}
if (row + 1 < src.rows) //Is this pixel not on the last row?
{
/* Bottom-Left
*
/ |
*--*
*/
writeFacetBeginning(stream);
stream.write("vertex " + row + " " + col + " " + pixelValues[row][col] * scale + "\n")
stream.write("vertex " +(row+1)+ " " +(col-1)+ " " + pixelValues[row + 1][col - 1] * scale + "\n")
stream.write("vertex " +(row+1)+ " " + col + " " + pixelValues[row + 1][col] * scale + "\n")
writeFacetEnd(stream);
/* Bottom-Left
*--*
| /
*
*/
writeFacetBeginning(stream);
stream.write("vertex " + row + " " + col + " " + pixelValues[row][col] * scale + "\n")
stream.write("vertex " + row + " " +(col-1)+ " " + pixelValues[row][col - 1] * scale + "\n")
stream.write("vertex " +(row+1)+ " " +(col-1)+ " " + pixelValues[row + 1][col - 1] * scale + "\n")
writeFacetEnd(stream);
}
if (row + 1 < src.rows &&(col+1)< src.cols)
{
/* Bottom-Right
*
| \
*--*
*/
writeFacetBeginning(stream);
stream.write("vertex " + row + " " + col + " " + pixelValues[row][col] * scale + "\n")
stream.write("vertex " +(row+1)+ " " + col + " " + pixelValues[row + 1][col] * scale + "\n")
stream.write("vertex " +(row+1)+ " " +(col+1)+ " " + pixelValues[row + 1][col + 1] * scale + "\n")
writeFacetEnd(stream);
/* Bottom-Right
*--*
\ |
*
*/
writeFacetBeginning(stream);
stream.write("vertex " + row + " " + col + " " + pixelValues[row][col] * scale + "\n")
stream.write("vertex " +(row+1)+ " " +(col+1)+ " " + pixelValues[row + 1][col + 1] * scale + "\n")
stream.write("vertex " + row + " " +(col+1)+ " " + pixelValues[row][col + 1] * scale + "\n")
writeFacetEnd(stream);
}
currentProgress += 4
}
updateProgressBar(currentProgress/maxProgress)
}
答案 0 :(得分:1)
在当前执行周期结束之前,浏览器不会呈现更改。因此,即使您更新了元素的宽度,浏览器也将等待执行周期来呈现更改,只有在您完成整个循环之后,该更改才会发生。
您可以在此处实施2种不同类型的解决方案
requestAnimationFrame
在呈现当前帧之后,在呈现下一帧之前执行回调。
function processRow(row = 1) {
for (let col = 1; col < src.cols; col+=2) {
// Your code
currentProgress += 4;
}
updateProgressBar(currentProgress/maxProgress);
if (row < src.rows) {
requestAnimationFrame(() => processRow(row + 2));
}
}
由于您不依赖DOM或对DOM进行任何更改来处理图像,因此,在完成循环时,将整个计算部分移至WebWorker并将消息发布到主线程。
在主线程中,您可以侦听来自WebWorker的消息并相应地更新进度条