jquery - 您可以使用 jquery 为 svg 过滤器镜面照明设置动画来简化整数吗?
问题描述
我正在使用feSpecularLighting
svg 过滤器,并且正在fePointLight
为鼠标在桌面上移动的位置设置动画。
<feSpecularLighting in="blur" surfaceScale="15" specularConstant="2.5" specularExponent="200" result="specOut" lighting-color="white">
<fePointLight x="-10000" y="-10000" z="8000" />
</feSpecularLighting>
我似乎找不到使用 CSS 过渡效果来缓解x
和y
属性更改的方法,我认为 svg 过滤器不会像这样工作。所以我想知道是否可以简化x
and的整数变化y
?
请参阅下面的工作示例,没有缓动和x
属性y
值。
// lighting effect variables
let lightDist = 10000;
let currentLightPos = {
x: -lightDist,
y: -lightDist
};
let currentMousePos = {
x: -1,
y: -1
};
let windowWidth = $(window).outerWidth();
let windowHeight = $(window).outerHeight();
let ratio = {
x: lightDist / (windowWidth / 2),
y: lightDist / (windowHeight / 2)
};
// when the window is resized
$(window).on('resize', function() {
// update lighting effect variables
windowWidth = $(window).outerWidth();
windowHeight = $(window).outerHeight();
ratio = {
x: lightDist / (windowWidth / 2),
y: lightDist / (windowHeight / 2)
};
});
// when the mouse is moved (desktop)
$(document).on('mousemove', function(e) {
// get our current mouse position
currentMousePos.x = e.pageX;
currentMousePos.y = e.pageY;
// if mouse is on right side of window
if (currentMousePos.x > (windowWidth / 2)) {
// calculate the positive light x position
currentLightPos.x = ratio.x * (currentMousePos.x - (windowWidth / 2));
// if mouse is on left side of window
} else if (currentMousePos.x < (windowWidth / 2)) {
// calculate the negative light x position
currentLightPos.x = -lightDist + (ratio.x * currentMousePos.x);
}
// calculate the negative light y position
// if mouse is on bottom side of window
if (currentMousePos.y > (windowHeight / 2)) {
// calculate the positive light y position
currentLightPos.y = ratio.y * (currentMousePos.y - (windowHeight / 2));
// if mouse is on top side of window
} else if (currentMousePos.y < (windowHeight / 2)) {
// calculate the negative light y position
currentLightPos.y = -lightDist + (ratio.y * currentMousePos.y);
}
// console.log(currentLightPos.x, currentLightPos.y);
// update shine filter fePointLight attributes
$('fePointLight').attr({
'x': currentLightPos.x,
'y': currentLightPos.y
});
});
.circle {
width: 100px;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
<svg class="circle" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100">
<circle cx="50" cy="50" r="50" filter="url(#shine)" />
</svg>
<svg xmlns="http://www.w3.org/2000/svg">
<filter id="shine" filterUnits="objectBoundingBox" x="-10%" y="-10%" width="150%" height="150%">
<feGaussianBlur in="SourceAlpha" stdDeviation="4" result="blur" />
<feSpecularLighting in="blur" surfaceScale="15" specularConstant="2.5" specularExponent="200" result="specOut" lighting-color="white">
<fePointLight x="-10000" y="-10000" z="8000" />
</feSpecularLighting>
<feComposite in="specOut" in2="SourceAlpha" operator="in" result="specOut2" />
<feComposite in="SourceGraphic" in2="specOut2" operator="arithmetic" k1="0" k2="1" k3="1" k4="0" />
</filter>
</svg>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
为了模拟我遇到的问题,如果您将鼠标移到示例窗口之外,然后将鼠标移回窗口的不同位置,您会注意到点光源会立即跳到新计算的位置。
我想知道是否有可能缓解这一点,所以x
和y
整数总是缓和到它们新计算的位置,而不是如果你将鼠标从另一侧带入窗口,而不是立即跳到新位置?
解决方案
(除此之外:虽然您的问题是“整数”,但<fePointLight>
属性也采用实数。所以这不会损害流畅的缓动。)
您可以通过两个步骤使用声明性 SMIL 动画来做到这一点:首先,您为两个声明两个<animate>
元素x
和. 确保此动画可以随时重新启动,即使动画当前正在运行。保留动画结束后的值。y
<fePointLight>
restart="always"
fill="freeze"
但主要的技巧是设置begin="indefinite"
. 这会推迟动画的开始,直到它被 Javascript API 调用触发。
<fePointLight x="-10000" y="-10000" z="8000">
<animate id="anim_x" attributeName="x"
begin="indefinite" dur="0.5s" restart="always"
from="-10000" to="-10000" fill="freeze" />
<animate id="anim_y" attributeName="y"
begin="indefinite" dur="0.5s" restart="always"
from="-10000" to="-10000" fill="freeze" />
</fePointLight>
我已将缓动功能保留为默认linear
值。对于更复杂的行为,语法比 CSS 缓动函数更复杂一些。类似的东西ease-in-out
看起来像这样:
<animate id="anim_x" attributeName="x"
begin="indefinite" dur="0.5s" restart="always"
calcMode="spline" values="-10000;-10000" keyTimes="0;1"
keySplines=".5 0 .5 1" fill="freeze" />
现在,在每次鼠标移动时,您的事件监听器都需要做三件事:
- 将属性设置为/属性
<animate from>
的当前动画值。这使得动画从当前值的任何位置开始,即使在运行动画期间也是如此。<fePointLight>
element.x.animVal
element.y.animVal
- 将
<animate to>
属性设置currentLightPos
为将动画移向其目标的值。 - 使用该方法启动动画
<fePointLight>
element.beginElement()
,取消任何当前正在运行的动画。
(如果您使用的是样条曲线,则必须设置values
属性而不是from
and to
。)
const pointLight = $('fePointLight');
const lightX = $('#anim_x');
const lightY = $('#anim_y');
lightX.attr({
from: pointLight.prop('x').animVal,
to: currentLightPos.x
});
lightX[0].beginElement();
lightY.attr({
from: pointLight.prop('y').animVal,
to: currentLightPos.y
});
lightY[0].beginElement();
这让点光源“追逐”当前鼠标位置,直到鼠标移动停止并且动画有机会运行到其结束。
// lighting effect variables
let lightDist = 10000;
let currentLightPos = {
x: -lightDist,
y: -lightDist
};
let currentMousePos = {
x: -1,
y: -1
};
let windowWidth = $(window).outerWidth();
let windowHeight = $(window).outerHeight();
let ratio = {
x: lightDist / (windowWidth / 2),
y: lightDist / (windowHeight / 2)
};
const pointLight = $('fePointLight');
const lightX = $('#anim_x');
const lightY = $('#anim_y');
// when the window is resized
$(window).on('resize', function() {
// update lighting effect variables
windowWidth = $(window).outerWidth();
windowHeight = $(window).outerHeight();
ratio = {
x: lightDist / (windowWidth / 2),
y: lightDist / (windowHeight / 2)
};
});
// when the mouse is moved (desktop)
$(document).on('mousemove', function(e) {
// get our current mouse position
currentMousePos.x = e.pageX;
currentMousePos.y = e.pageY;
// if mouse is on right side of window
if (currentMousePos.x > (windowWidth / 2)) {
// calculate the positive light x position
currentLightPos.x = ratio.x * (currentMousePos.x - (windowWidth / 2));
// if mouse is on left side of window
} else if (currentMousePos.x < (windowWidth / 2)) {
// calculate the negative light x position
currentLightPos.x = -lightDist + (ratio.x * currentMousePos.x);
}
// calculate the negative light y position
// if mouse is on bottom side of window
if (currentMousePos.y > (windowHeight / 2)) {
// calculate the positive light y position
currentLightPos.y = ratio.y * (currentMousePos.y - (windowHeight / 2));
// if mouse is on top side of window
} else if (currentMousePos.y < (windowHeight / 2)) {
// calculate the negative light y position
currentLightPos.y = -lightDist + (ratio.y * currentMousePos.y);
}
// console.log(currentLightPos.x, currentLightPos.y);
// update shine filter fePointLight attributes
lightX.attr({
from: pointLight.prop('x').animVal,
to: currentLightPos.x
});
lightX[0].beginElement();
lightY.attr({
from: pointLight.prop('y').animVal,
to: currentLightPos.y
});
lightY[0].beginElement();
});
.circle {
width: 100px;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
<svg class="circle" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100">
<circle cx="50" cy="50" r="50" filter="url(#shine)" />
</svg>
<svg xmlns="http://www.w3.org/2000/svg">
<filter id="shine" filterUnits="objectBoundingBox" x="-10%" y="-10%" width="150%" height="150%">
<feGaussianBlur in="SourceAlpha" stdDeviation="4" result="blur" />
<feSpecularLighting in="blur" surfaceScale="15" specularConstant="2.5" specularExponent="200" result="specOut" lighting-color="white">
<fePointLight x="-10000" y="-10000" z="8000">
<animate id="anim_x" attributeName="x"
begin="indefinite" dur="0.5s" restart="always"
from="-10000" to="-10000" fill="freeze" />
<animate id="anim_y" attributeName="y"
begin="indefinite" dur="0.5s" restart="always"
from="-10000" to="-10000" fill="freeze" />
</fePointLight>
</feSpecularLighting>
<feComposite in="specOut" in2="SourceAlpha" operator="in" result="specOut2" />
<feComposite in="SourceGraphic" in2="specOut2" operator="arithmetic" k1="0" k2="1" k3="1" k4="0" />
</filter>
</svg>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
推荐阅读
- php - PHP Gmail API:尝试仅收集最后 100 个线程
- c++ - 如何让我的 Arduino 停止发送混乱的 Unicode 而不是数字?
- sql - 分组到列并填充具有唯一排名行的列
- amazon-s3 - 如何使用 CloudFront 从 S3 提供静态文件
- c# - 更改枚举菜单名称格式 [C#]
- python - 如何使程序在python中的空白行结束?
- machine-learning - 关于使用 LSTM 模型处理非常长的输入的实用建议?
- parsing - 尝试加载数据时出现键错误
- templates - Jinja2模板数组中是否存在值?
- android - Android Studio 是否仍然编译未使用的导入库?