我正在開發一個用HTML/CSS/javascript實現的臺球記分應用程序: https://github.com/Zenilogix-Carl/Site
我一直在使用Windows和Chrome進行開發,得到了預期的結果,或者至少能夠在那個環境中診斷問題。我看到它在一些朋友的iPhones上運行,我得到了非常不同的(和有缺陷的)結果。我希望有人能幫我識別并糾正影響iPhone的問題。
與我的問題相關的三個文件是:
NineBall.html 臺球. js 樣式. css 下面附上了截圖。注意,在Chrome中,所有的球都用投影渲染;這是在BilliardBall類中定義的,并擴展到BilliardBallWithState(都在臺球. js中),根據當前狀態,可以通過NineBall.html的函數updateBallState打開或關閉。初始狀態應該顯示所有球的投影,這在Chrome中可以,但在iPhone上不能。
class BilliardBall {
constructor(number, size, allowForShadow) {
var colors = ["yellow", "blue", "red", "purple", "orange", "green", "brown", "var(--ballBlack)"];
var color = Number.isInteger(number) ? colors[(number - 1) % 8] : colors[0];
var isStripe = Number.isInteger(number) ? (((number - 1) / 8) >= 1) : false;
this.number = number;
var svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
svg.setAttribute("viewBox", allowForShadow ? "0 0 120 120" : "0 0 105 100");
svg.setAttribute("width", size);
svg.setAttribute("preserveAspectRatio", "xMidYMid meet");
var g = document.createElementNS("http://www.w3.org/2000/svg", "g");
this.ballGraphic = g;
var circle = document.createElementNS("http://www.w3.org/2000/svg", "circle");
if (isStripe) {
circle.setAttribute("cx", 50);
circle.setAttribute("cy", 50);
circle.setAttribute("r", 48);
circle.setAttribute("style", "fill: white;");
g.appendChild(circle);
var path = document.createElementNS("http://www.w3.org/2000/svg", "path");
path.setAttribute("d", "M 16 16 L 84 16 A 50 50 0 0 1 84 84 L 16 84 A 50 50 0 0 1 16 16 ");
path.setAttribute("style", "fill: " + color + "; stroke-width: 1; stroke: grey;");
g.appendChild(path);
circle = document.createElementNS("http://www.w3.org/2000/svg", "circle");
circle.setAttribute("cx", 50);
circle.setAttribute("cy", 50);
circle.setAttribute("r", 48);
circle.setAttribute("style", "fill: transparent; stroke-width: 1; stroke: var(--ballOutline);");
g.appendChild(circle);
} else {
circle.setAttribute("cx", 50);
circle.setAttribute("cy", 50);
circle.setAttribute("r", 48);
circle.setAttribute("style", `fill: ${color}; stroke-width: 1; stroke: var(--ballOutline);`);
g.appendChild(circle);
}
circle = document.createElementNS("http://www.w3.org/2000/svg", "circle");
circle.setAttribute("cx", 50);
circle.setAttribute("cy", 50);
circle.setAttribute("r", 27);
circle.setAttribute("style", "fill: white; stroke-width: 1; stroke: grey;");
g.appendChild(circle);
var text = document.createElementNS("http://www.w3.org/2000/svg", "text");
text.setAttribute("x", 50);
text.setAttribute("y", 53);
text.setAttribute("text-anchor", "middle");
text.setAttribute("dominant-baseline", "middle");
text.setAttribute("font-weight", "bold");
text.setAttribute("font-size", number > 9 ? "32px" : "40px");
text.innerHTML = number;
g.appendChild(text);
svg.appendChild(g);
this.element = svg;
}
}
class BilliardBallWithState extends BilliardBall {
constructor(number, size, clickFn, foulText) {
super(number, size, true);
const svg = this.element;
svg.onclick = function () { clickFn(number)};
let text = document.createElementNS("http://www.w3.org/2000/svg", "text");
text.setAttribute("style", "visibility: hidden;");
text.classList.add("dropShadow");
text.setAttribute("x", 50);
text.setAttribute("y", 50);
text.setAttribute("text-anchor", "middle");
text.setAttribute("dominant-baseline", "middle");
text.setAttribute("font-size", "30px");
text.setAttribute("fill", "red");
text.setAttribute("stroke-width", "1");
text.setAttribute("stroke", "white");
text.innerHTML = foulText;
svg.appendChild(text);
this.foulText = text;
text = document.createElementNS("http://www.w3.org/2000/svg", "text");
text.setAttribute("style", "visibility: hidden;");
text.classList.add("dropShadow");
text.setAttribute("x", 40);
text.setAttribute("y", 90);
text.setAttribute("font-size", "80px");
text.setAttribute("fill", "green");
text.setAttribute("stroke-width", "1");
text.setAttribute("stroke", "white");
text.innerHTML = "✔";
svg.appendChild(text);
this.checkMark = text;
this.showNormal();
}
dimElement(elem, dim) {
if (dim) {
elem.classList.remove("dropShadow");
elem.classList.add("dimmed");
} else {
elem.classList.add("dropShadow");
elem.classList.remove("dimmed");
}
}
showElement(elem, show) {
elem.style.visibility = show ? "visible" : "hidden";
}
showNormal() {
this.dimElement(this.ballGraphic, false);
this.showElement(this.foulText, false);
this.showElement(this.checkMark, false);
}
showPocketed(checked) {
this.dimElement(this.ballGraphic, true);
this.showElement(this.foulText, false);
this.showElement(this.checkMark, checked);
}
showFoul() {
this.dimElement(this.ballGraphic, true);
this.showElement(this.foulText, true);
this.showElement(this.checkMark, false);
}
}
function updateBallState(number) {
const currentBallState = match.ballStates[number - 1];
const ball = balls[number];
switch (currentBallState) {
case "normal":
ball.showNormal();
break;
case "dead":
ball.showFoul();
break;
default:
ball.showPocketed(currentBallState === "won");
break;
}
}
此外,雖然沒有反映在屏幕上,但似乎cookies在iPhone上不能正常工作,所以我想知道我是否正確地處理了它們——參見NineBall.html的onLoad函數和臺球. js中的Preferences類
我需要了解我正在做的事情在各種瀏覽器中沒有功能/統一支持,以及我需要做什么才能讓我的應用程序在Android (Chrome)和iPhone(大概是Safari)瀏覽器中正確運行。
鍍鉻屏幕帽(預期外觀):
iPhone屏幕帽(顯示異常,如上所述):
編輯
增加了一個截圖來說明我的目標是不同的臺球顯示狀態。這是在Chrome上,所以顯示了我所期望的。請注意灰色狀態如何與復選標記或& quot死亡& quot文本,兩者都不會變暗,并且有陰影幫助突出顯示。我提到這一點是因為它與單個組件元素可以或不可以分組進行樣式化的方式有關。
實際上,您可以將css樣式應用到SVG & ltg & gt可以繼承的元素——但是顯然webkit不接受應用于SVG子元素的css過濾器。
我可以在Epiphany/Web和美島莉中重現這個渲染問題(運行在虛擬的Linux Mint環境中)。
解決方法:CSS父級陰影& ltsvg & gt子元素的SVG過濾器 正如Clint Warner所建議的,你可以將CSS陰影應用到外部& ltsvg & gt元素。
內部子元素可以使用本地SVG過濾器。
您可以添加一個包含本地svg過濾器的不可見svg,如下所示:
<svg height="0" width="0">
<filter y="-50%" height="250%" id='dropshadowSvg' color-interpolation-filters="sRGB">
<feDropShadow dx="5" dy="5" stdDeviation="5" flood-opacity="1" />
</filter>
</svg>
并通過css規則/內聯樣式應用它
.dropShadowSVG {
filter: url(#dropshadowSvg);
}
:root{
--background: lightblue;
--popupBackground: white;
--foreground: black;
--dropShadowFilter: drop-shadow(10px 10px 5px black);
--ballOutline: grey;
--ballBlack: black;
}
svg{
overflow:visible;
width:25%;
}
.dropShadowCSS {
filter: var(--dropShadowFilter);
}
.dropShadowSVG {
filter: url(#dropshadowSvg);
}
<svg height="0" width="0">
<filter y="-50%" height="250%" id='dropshadowSvg' color-interpolation-filters="sRGB">
<feDropShadow dx="5" dy="5" stdDeviation="5" flood-opacity="1" />
</filter>
</svg>
<div id="balls" style="padding: 10px;">
<svg viewBox="0 0 120 120" class="dropShadowCSS">
<g>
<circle cx="50" cy="50" r="48" style="fill: yellow; stroke-width: 1; stroke: var(--ballOutline);"></circle>
<circle cx="50" cy="50" r="27" style="fill: white; stroke-width: 1; stroke: grey;"></circle><text x="50" y="53" text-anchor="middle" dominant-baseline="middle" font-weight="bold" font-size="40px">?</text>
</g><text style="visibility: visible;" class="dropShadow" x="50" y="75" text-anchor="middle" dominant-baseline="middle" font-size="30px" fill="white" stroke-width="1" stroke="white"></text><text style="visibility: hidden;" class="dropShadow" x="40" y="90" font-size="80px" fill="green" stroke-width="1" stroke="white">?</text>
</svg>
<svg viewBox="0 0 120 120" class="dropShadowCSS">
<g style="color:purple">
<circle cx="50" cy="50" r="48" style="fill: #ccc; stroke-width: 1; stroke: grey ;">
</circle>
<text class="dropShadowSVG" x="50" y="50" text-anchor="middle" dominant-baseline="middle" font-size="20px" font-weight="bold" fill="currentColor" stroke-width="1">Time-out</text>
</g>
</svg>
<svg viewBox="0 0 120 120" class="dropShadowCSS">
<g style="color:purple">
<circle cx="50" cy="50" r="48" style="fill: #ccc; stroke-width: 1; stroke: grey ;">
</circle>
<text class="dropShadowCSS" x="50" y="50" text-anchor="middle" dominant-baseline="middle" font-size="20px" font-weight="bold" fill="currentColor" stroke-width="1">Time-out</text>
</g>
</svg>
</div>
更新2:
看了這個答案的評論,了解了更多你對這個項目的目標,我相信我已經找到了一個可以接受的解決你問題的方法。
看起來你把效果和疊加元素放在了一起,但是我認為它們是不同的問題。我下面提出的解決方案可以解決效果問題。通過將自定義類應用到svg元素本身,您應該能夠以瀏覽器安全的方式應用自定義效果。
然而,對于疊加的元素,我認為最好的方法實際上是一個單獨的元素疊加在臺球上。在我看來,你的很多疊加元素都被重用了。我建議將重用的元素保存為站點資源,并作為偽元素應用于單獨的HTML元素或背景圖像。下面的代碼還沒有經過測試,但它旨在更清楚地展示我的主張:
/* General styling. `position: relative;` needed on a parent to correctly set the context for an absolutely positioned child */
.ball {
position: relative;
width: 100px;
height: 100px;
padding: 10px;
}
/* Applies to all SVG elements under .ball container */
.ball svg {
width: 100%;
height: 100%;
border-radius: 50%;
background-color: green;
}
/* Applies drop shadow to the applicable SVG */
svg.dropShadow {
box-shadow: 10px 5px 5px black;
}
/* Properly dims applicable SVGs */
svg.dimmed {
filter: brightness(50%);
}
/* "Check" in this case is a small red circle that is overlaid on the dimmed SVG */
svg.check {
position: absolute;
top: 25%;
left: 25%;
background-color: red;
z-index: 2;
width: 50%;
height: 50%;
}
<div class="balls">
<!-- Default ball behavior -->
<div class="ball">
<svg class="dropShadow">
</svg>
</div>
<!-- Dimmed and checked ball behavior -->
<div class="ball">
<!-- The `check` class would absolutely position this element over and in front of the ball itself -->
<svg class="check">
</svg>
<svg class="dimmed">
</svg>
</div>
</div>