How To Convert A List To A String In Python (With Examples)
Source link
برچسب: With
-
JavaScript Location.reload() Explained (With Examples)
In modern web development, there are times when a page needs to refresh itself without the user pressing a button. Whether you are responding to updated content, clearing form inputs, or forcing a session reset, JavaScript provides a simple method for this task:
location.reload()
.This built-in method belongs to the
window.location
object and allows developers to programmatically reload the current web page. It is a concise and effective way to refresh a page under controlled conditions, without relying on user interaction.What Is JavaScript
location.reload()
?The
location.reload()
method refreshes the page it is called on. In essence, it behaves the same way a user would if they clicked the browser’s reload button. However, because it is called with JavaScript, the action can be triggered automatically or in response to specific events.Here is the most basic usage:
location.reload();
This line of code tells the browser to reload the current page. It does not require any parameters by default and typically loads the page from the browser’s cache. Note that you can use our free resources (namely, online code editors) to follow along with this discussion.
Forcing a Hard Reload
Sometimes a regular reload is not enough, especially when you want to ensure that the browser fetches the latest version of the file from the server instead of using the cached copy. You can force a hard reload by passing
true
as a parameter:location.reload(true);
However, it is important to note that modern browsers have deprecated this parameter in many cases. Instead, they treat all reloads the same. If you need to fully bypass the cache, server-side headers or a versioned URL might be a more reliable approach.
And let’s talk syntax:
So what about the false parameter? That reloads the page using the web browser cache. Note that false is also the default parameter. So if you run reload() without a parameter, you’re actually running object.reload(false). This is covered in the Mozilla developer docs.
So when do you use Location.reload(true)? One common situation is when the page has outdated information. A hard reload can also bypass caching issues on the client side.
Common Use Cases
The
location.reload()
method is used across a wide range of situations. Here are a few specific scenarios where it’s especially useful:1. Reload after a form submission:
document.getElementById("myForm").onsubmit = function() { setTimeout(function() { location.reload(); }, 1000); };
This use case helps clear form inputs or reset the page state after the form has been processed. You can test this in the online Javascript editor. No download required. Just enter the code and click run to immediately see how it looks.
2. Refresh after receiving new data:
In web applications that rely on live data, such as dashboards or status monitors, developers might use
location.reload()
to ensure the page displays the most current information after an update.3. Making a manual refresh button:
<button onclick="location.reload();">Refresh Page</button>
This is a simple way to give users control over when to reload, particularly in apps that fetch new content periodically.
4. Reload a Page Without Keeping the Current Page in Session History
This is another common use. It looks like this.
window.location.replace(window.location.href);
Basically, if a user presses the back button after they hit reload, they might be taken back to a page that no longer reflects the current application logic. The widow.location.replace() method navigates to a new URL, often the same one, and replaces the current page in the session history.
This effectively reloads the page without leaving a trace in the user’s history stack. It is particularly useful for login redirects, post-submission screens, or any scenario where you want to reset the page without allowing users to revisit the previous state using the back button.
Limitations and Best Practices
While
location.reload()
is useful; it should be used thoughtfully. Frequent or automatic reloads can frustrate users, especially if they disrupt input or navigation. In modern development, reloading an entire page is sometimes considered a heavy-handed approach.For dynamic updates, using JavaScript to update only part of the page, through DOM manipulation or asynchronous fetch requests, is often more efficient and user-friendly.
Also, keep in mind that reloading clears unsaved user input and resets page state. It can also cause data to be resubmitted if the page was loaded through a form POST, which may trigger browser warnings or duplicate actions. If you’re looking for a job, make sure to brush up on this and any other common JavaScript interview questions.
Smarter Alternatives to Reloading the Page
While
location.reload()
is simple and effective, it is often more efficient to update only part of a page rather than reloading the entire thing. Reloading can interrupt the user experience, clear form inputs, and lead to unnecessary data usage. In many cases, developers turn to asynchronous techniques that allow content to be refreshed behind the scenes.AJAX, which stands for Asynchronous JavaScript and XML, was one of the earliest ways to perform background data transfers without refreshing the page. It allows a web page to send or receive data from a server and update only the necessary parts of the interface. Although the term AJAX often brings to mind older syntax and XML data formats, the concept remains vital and is now commonly used with JSON and modern JavaScript methods.
One of the most popular modern approaches is the Fetch API. Introduced as a cleaner and more flexible alternative to
XMLHttpRequest
, the Fetch API uses promises to handle asynchronous requests. It allows developers to retrieve or send data from a server and then apply those updates directly to the page using the Document Object Model, or DOM.Here is a simple example:
fetch('/api/data') .then(response => response.json()) .then(data => { document.getElementById('content').textContent = data.message; });
This example retrieves data from the server and updates only a single element on the page. It is fast, efficient, and keeps the user interface responsive.
By using AJAX or the Fetch API, developers can create a more fluid and interactive experience. These tools allow for partial updates, background syncing, and real-time features without forcing users to wait for an entire page to reload. In a world where performance and responsiveness matter more than ever, these alternatives offer a more refined approach to managing content updates on the web.
Conclusion
The
location.reload()
method in JavaScript is a straightforward way to refresh the current web page. Whether used for resetting the interface or updating content, it offers a quick and accessible solution for common front-end challenges. But like all tools in web development, it should be used with an understanding of its impact on user experience.Before reaching for a full page reload, consider whether updating the page’s content directly might serve your users better. When applied appropriately,
location.reload()
can be a useful addition to your JavaScript toolkit.Want to put this into action? Add it to a JavaScript project and test it out.
-
HTML Editor Online with Instant Preview and Zero Setup
HTML Editor Online with Instant Preview and Zero Setup
Source link -
Write and Test Code Instantly With an Online Python Editor
Write and Test Code Instantly With an Online Python Editor
Source link -
Kimsuky APT Targets South Korea with Deceptive PDF Lures
Kimsuky: A Continuous Threat to South Korea with Deceptive Tactics
Contents
- Introduction
- Infection Chain
- Initial Findings
- Campaign 1
- Looking into PDF document.
- Campaign 2
- Looking into PDF document.
- Technical Analysis
- Conclusion
- Seqrite Protection
- MITRE ATT&CK
- IOCs
Introduction:
Security researchers at Seqrite Labs have recently uncovered two distinct campaigns carried out by the APT group “Kimsuky,” also known as “Black Banshee.” This group has been actively targeting South Korea using evolving tactics. In these campaigns, the threat actors delivered two South Korean government-themed documents as lures, specifically targeting government entities within South Korea.
In this blog, we will delve into the technical details of the campaigns uncovered during our analysis. We will examine the various stages of infection, starting with a phishing email containing an LNK (shortcut) file attachment. The LNK file was designed to drop an obfuscated VBA (Visual Basic for Applications) script, After de-obfuscating the script, we found that it was responsible for dropping two additional files: One Pdf file and One ZIP file The ZIP file contained four malicious files: two log files (1.log and 2.log), one VBA script (1.vba), and one PowerShell script (1.ps1). Both campaigns involved the same set of malicious files.
Infection Chain:
Fig .1 infection chain Initial Findings:
Campaign-1:
In the first campaign, we identified a document related to tax reduction and tax payment related to revenue, which contained the same malicious LNK attachment. This attachment subsequently deployed a malicious VBScript, facilitating further compromise.
Fig .2 Revanue.pdf file Based on our initial findings, we discovered that the adversary utilized a different document containing the same LNK file content.
Campaign-2:
In campaign-2, it has come to our attention that South Korea has enacted a new policy aimed at preventing recidivism among sex offenders. The initiative involves circulating a detailed document outlining the regulations, which was shared with households, daycare centers, kindergartens, and various local administrative offices, including township and village authorities, as well as neighbourhood community centres. However, hackers, including cyber-criminals, are exploiting this dissemination process by sending deceptive emails containing harmful attachments. These emails are targeting residential recipients and key personnel at local offices.
Fig .3 Sex Offender Personal Information Notification.pdf The adversaries have exploited the distribution of this information and document by circulating it via email, disguised under the filename 성범죄자 신상정보 고지.pdf.lnk (Sex Offender Personal Information Notification.pdf.lnk). This attachment contains a malicious LNK file, which poses a cybersecurity threat to the recipients.
Technical Analysis and Methodology:
Campaign 1 & 2:
We have downloaded the file named 28f2fcece68822c38e72310c911ef007f8bd8fd711f2080844f666b7f371e9e1.lnk from campaign-1 and “성범죄자 신상정보 고지.pdf.lnk” from campaign-2 (Sex Offender Personal Information Notification.pdf.lnk) that was shared via email. During the analysis of this LNK file, it appears to be fetching additional files from an external C2 server, as shown in the snapshot below.
Fig.4 Downloading VBScript from C2 (Campaign –1) Fig .5 Downloading VBScript From C2 (Campaign -2) The file was downloaded from the URL provided above and saved into the Temp folder, as indicated below.
Fig .6 downloaded into Temp Folder (Campaign-1) Fig .7 downloaded into Temp Folder (Campaign-2) The file downloaded from the C2 server appears to be an obfuscated VBScript. Upon DE obfuscating the script, we discovered two additional files: one PDF and one ZIP file.
Fig .8 Obfuscated VB Script The first section of the file is encoded in Base64 strings.
Fig .9 Base64 Encoded PDF After Decoding we have found one PDF file.
Fig .10 PDF after Decoding The second part of the VBScript is also encoded in Base64. After decoding it, we discovered a ZIP file.
Fig .11 Zip File Fig. 12 Detect It Easy Zip files contain the below numbers of files in it.
Fig .13 Inside Zip File Within the ZIP archive, four files were identified: a VBScript, a PowerShell script, and two Base64-encoded text files. These encoded text files house obfuscated data, which, upon further dissection, may yield critical intelligence regarding the malware’s functionality and objectives. The following figures illustrate the encoded content of the two text files, which will be subsequently decoded and analysed to elucidate the next phase in the attack chain.
Fig. 14- 1 Log.txt file with Base64 encoding Fig.15 – 2 Log .txt file with Base64 encoding The 1.vbs file employs advanced obfuscation techniques, utilizing the chr() and CLng() functions to dynamically construct characters and invoke commands at runtime. This strategy effectively circumvents signature-based detection mechanisms, allowing the script to evade detection during execution.
Upon script termination, the concatenated characters form a complete command, which is subsequently executed. This command is likely designed to invoke the 1.ps1 PowerShell script, passing 1.log as an argument for further processing.
Fig .16 – 1.vbs Upon attempting to DE-obfuscate the VBScript, we uncovered the following command-line execution, which subsequently triggers the PowerShell script for further processing.
Fig .17 De-Obfuscated VB Script Upon executing the 1.vbs file, it triggered the invocation of the 1.ps1 file, as illustrated in the snapshot below.
Fig .18 Executing 1.VBS The 1.ps1 script includes a function designed to decode Base64-encoded data from the 1.log file and execute the resulting script.
Fig.19 – 1.ps1 file Fig.20 – 1 Log.txt after decoding The 1.ps1 script retrieves the BIOS serial number, a unique system identifier, from the compromised host. This serial number is subsequently used to create a dedicated directory within the system’s temporary folder, ensuring that attack-related files are stored in a location specific to the compromised machine, as shown in above snapshot.
As a VM-aware sample, the script checks if it is executing within a virtual machine environment. If it detects a virtual machine, it will delete all four files associated with the attack (1.vbs, 1.ps1, 1.log, and any payload files stored in the directory named after the serial number), effectively halting its execution, as illustrated.
The script encompasses 11 functions that define the subsequent phases of the malware’s operation, which include data exfiltration, cryptocurrency wallet information theft, and the establishment of Command-and-Control (C2) communications. These functions are integral to the attack’s execution, facilitating the malware’s objectives and ensuring persistent communication with the threat actor.
List of malicious function retrieved from 1 log file:
- UploadFile ():
The upload function exfiltrates data by transmitting it to the server in 1MB chunks, allowing it to handle large file sizes efficiently. The script awaits a response from the server, and if it receives an HTTP status code of “200,” it proceeds with further execution. If the response differs, the script terminates its operation. Each chunk is sent via an HTTP POST request, with the function verifying the success of each upload iteration before continuing.
Fig .21 UploadFile() - GetExWFile ():
The GetExWFile function iterates through a set of predefined hash tables containing cryptocurrency wallet extensions. When a match is found, it identifies the associated”.ldb” and ”.log” files linked to those extensions for exfiltration. These files are subsequently transferred to the specified destination folder, as indicated by the $Storepath variable.
Fig.22 GetExWFile () - GetBrowserData ():
The script checks whether any of the following browsers—Edge, Firefox, Chrome, or Naver Whale—are actively running, to extract user profile data, including cookies, login credentials, bookmarks, and web data. Prior to collecting this information, the script terminates the browser processes to ensure uninterrupted access. It then proceeds to retrieve data on installed extensions and cache files, such as webcacheV01.dat, for each identified browser. For certain browsers, it also performs decryption operations to unlock encrypted keys, allowing it to extract sensitive information, which is then stored alongside the decrypted master encryption key.
Fig.23 BrowserData () - Download file () :
The download file function downloads any file based on the C2 command.
Fig.24 Download File () - RegisterTask () :
It creates persistence for the files “1.log” and “1.vbs”.
Fig.25 RegisterTask() - Send ():
The send () function uploads all the collected information to the server after compressing the data into a ZIP file named “init.zip”. It then renames the ZIP file to “init.dat” and deletes all backup files from the system after uploading.
Fig.26 Send () The execution flow of the functions follows a sequence where several actions are carried out within the attack. Among these functions, one triggers another PowerShell command that calls the 2.log file, which is responsible for performing keylogging activities.
Fig. 27 Flow of execution of functions and command to execute “2.log”. Fig.28 Executing 2 log file Fig.29 Inside 2 log file The decoded content of the 2.log file is shown above. It contains a script that imports essential Windows API functions for detecting key presses, retrieving window titles, and managing keyboard states. The script executes actions such as clipboard monitoring, keystroke logging, and recording window titles.
Fig. 30.2 Code for clipboard monitoring. Conclusion
As observed, threat actors are utilizing time-consuming, multi-component techniques that are interlinked to enhance their evasiveness. Unlike other stealers, this one primarily focuses on network-related information, which could be leveraged for active reconnaissance. Given that the stealer targets sensitive user data, it is crucial to protect yourself with a reputable security solution such as Seqrite Antivirus in today’s digital landscape. At Seqrite Lab, we provide detection capabilities for such stealers at various stages of infection, along with protection against the latest threats.
Seqrite Protection:
- Trojan.49424.SL
- Trojan.49422.C
MITRE ATT&CK:
Initial Access T1566.001 Phishing: Spearphishing Attachment Execution T1059.001 T1059.005
Command and Scripting Interpreter: PowerShell Command and Scripting Interpreter: Visual Basic
Persistence T1547.001 Boot or Logon Autostart Execution: Registry Run Keys / Startup Folder Defense Evasion T1140 Deobfuscate/Decode Files or Information Credential Access T1555.003 Credentials from Password Stores: Credentials from Web Browsers Discovery T1082 System Information Discovery Collection T1056.001 Input Capture: Keylogging Command and Control T1071.001 Application Layer Protocol: Web Protocols Exfiltration T1041 Exfiltration Over C2 Channel IoCs:
MD5 File Name 1119A977A925CA17B554DCED2CBABD8 *.lnk 64677CAE14A2EC4D393A81548417B61B 1.log F0F63808E17994E91FD397E3A54A80CB 2.log A3353EA094F45915408065D03AE157C4 prevenue.hta CE4549607E46E656D8E019624D5036C1 1.vbs 1B90EFF0B4F54DA72B19195489C3AF6C *.lnk 1D64508B384E928046887DD9CB32C2AC 성범죄자 신상정보 고지.pdf.lnk C2
- hxxps[:]//cdn[.]glitch[.]global/
- hxxp[:]//srvdown[.]ddns.net
Authors
Dixit Panchal
Kartik Jivani
Soumen Burma
-
Mastering Carousels with GSAP: From Basics to Advanced Animation
Carousels are a fairly common UI pattern (there are many excellent carousel and slider examples available on Codrops). While carousel designs vary depending on the use case, the following demos explore how the GreenSock Animation Platform (GSAP) can be used to achieve seamless looping, smooth animations, and ultimately, a better user experience.
This article is for frontend designers and developers interested in enhancing the functionality and visual appeal of a standard horizontal carousel. Familiarity with JavaScript and basic GSAP methods will be helpful, but anyone looking for inspiration and practical examples may find the following content useful.
What You’ll Learn
- Basic carousel implementation using HTML and CSS
- How to use
gsap.utils.wrap()
andhorizontalLoop()
- Advanced animation techniques, including image parallax and function-based values
Our Basic Carousel
Let’s start with a horizontally scrolling carousel using only HTML and CSS:
<div class="carousel"> <div class="carousel-slide"> <img src="https://images.unsplash.com/photo-1659733582156-d2a11801e59f?q=50&w=1600"> <h2>We're No</h2> <h5>Strangers to love</h5> </div> ... </div>
.carousel { width: 100vw; height: 80vh; gap: 10px; overflow-x: auto; scroll-snap-type: x mandatory; display: flex; -webkit-overflow-scrolling: touch; } .carousel-slide { position: relative; flex: 0 0 50%; display: flex; flex-direction: column; justify-content: center; align-items: center; color: white; scroll-snap-align: center; overflow: hidden; } .carousel-slide img { position: absolute; width: 100%; height: 100%; object-fit: cover; } h2 { position: relative; margin: 0; font-size: 1.8rem; } h5 { position: relative; margin: 2% 0 0 0; font-size: 1rem; font-weight: 100; letter-spacing: 0.3px; } /* Simplify the scroll bar appearance */ ::-webkit-scrollbar { height: 13px; } ::-webkit-scrollbar-track { background: transparent; } ::-webkit-scrollbar-thumb { border-top: 6px solid #000; background: #555; width: 50%; } ::-webkit-scrollbar-thumb:hover { background: #bbb; } @media (max-width: 500px) { .carousel-slide { flex: 0 0 80%; } ::-webkit-scrollbar-thumb { width: 80%; } }
Here’s the result:
It uses scroll snapping and some custom styling on the scrollbar. Nothing fancy, but it works even when JavaScript is disabled.
Note that the HTML above is intentionally concise. However, in production, it’s important to follow accessibility best practices, including using alt text on images and descriptive ARIA attributes for screen reader users.
Building on the Foundation – GSAP Demo 1A
To see how GSAP can enhance a carousel, we’ll explore two different approaches—the first using gsap.utils.wrap(). Wrap is one of several handy utility methods included in gsap.js—no plugin required! Given a min/max range, it returns a value within that range:
gsap.utils.wrap(5, 10, 12); // min 5, max 10, value to wrap 12: returns 7
The example above returns 7 because 12 is 2 more than the maximum of 10, so it wraps around to the start and moves 2 steps forward from there. In a carousel, this can be used to loop infinitely through the slides.
Here’s a simple demo of how it can be applied:
In the HTML, a
<nav>
block has been added that contains previous/next buttons and progress text:<nav class="carousel-nav"> <button class="prev" tabindex="0" aria-label="Previous Slide"></button> <button class="next" tabindex="0" aria-label="Next Slide"></button> <div>1/8</div> </nav>
A few new rules have been added to the CSS, most importantly to
.carousel-slide-abs
:.carousel-slide-abs { position: absolute; left: 50%; top: 50%; transform: translate(-50%, -50%); width: 75vw; height: 70vh; }
In the JS, we override the carousel’s
scroll-snap-type
and display the<nav>
block. Since we no longer have a scrollable area, the buttons are necessary to maintain keyboard accessibility. Safari requirestabindex="0"
to allow users to tab to them. Additionally,aria-labels
are important since the buttons have no visible text content.We apply the new class to each slide, which effectively stacks them all in the center. We also set the initial opacity: 1 for the first slide and 0 for the rest:
gsap.set(".carousel", { "scroll-snap-type": "none" }); gsap.set(".carousel-nav", { display: "block" }); slides.forEach((slide, i) => { slide.classList.add("carousel-slide-abs"); gsap.set(slide, { opacity: (i === 0 ? 1 : 0) }); });
Next, we need a function that transitions to the previous or next slide.
changeSlide()
is passed a direction parameter of either positive or negative 1. Inside this function, we:- Fade out the current slide
- Update the current slide index using
gsap.utils.wrap()
- Fade in the new current slide
- Update the progress text
The different easing on the outro and intro tweens helps prevent excessive overlapping opacity during the crossfade.
next.addEventListener("click", () => changeSlide( 1 )); prev.addEventListener("click", () => changeSlide( -1 )); function changeSlide( dir ) { gsap.to(slides[currentIndex], { opacity: 0, ease: "power3" }); currentIndex = gsap.utils.wrap(0, slides.length, (currentIndex += dir)); gsap.to(slides[currentIndex], { opacity: 1, ease: "power3.inOut" }); gsap.set(".carousel-nav div", { innerText: `${currentIndex + 1}/${slides.length}` }); }
Polishing the Transition – GSAP Demo 1B
To take this idea further, let’s add more detail to the outro and intro animations:
For the 3D perspective to work, we’ve added
perspective: 750px
to.carousel-slide-abs
in the CSS.Instead of targeting the slides themselves, we set the opacity of their child elements to 0—except for those in the first slide.
gsap.set(slide.children, { opacity: (i === 0 ? 1 : 0) });
Then, we do the following inside
changeSlide()
:- Store a reference to the outgoing slide’s children
- Update
currentIndex
, just as before - Create a
const
for the incoming slide’s children - Kill tweens on both slides’ children to prevent conflicts if slides change rapidly
- Create a timeline for the transition:
gsap.timeline({ defaults:{ ease: "expo" } }) // update progress text .set(".carousel-nav div", { innerText: `${currentIndex + 1}/${slides.length}` }) // old slide outro .to(oldLayers[0], { duration: 0.3, rotateY: (dir<0 ? -75 : 75), scale: 0.6, ease: "power2.in" }, 0) .to(oldLayers, { duration: 0.3, opacity: 0, ease: "power2.in" }, 0) // new slide intro .to(newLayers, { opacity: 1, ease: "power1.inOut", stagger: 0.2 }, 0.2) .fromTo(newLayers[0], { rotateY: (dir<0 ? 90 : -90), scale: 0.6 },{ rotateY: 0, scale: 1 }, 0.3) .fromTo([newLayers[1], newLayers[2]], { y: 35 },{ duration: 1, y: 0, stagger: 0.14 }, 0.4);
Easing and staggers help smooth out and space the movement. The
dir
parameter modifies therotationY
, adding a subtly unique motion to previous and next actions.This basic setup can be easily customized further. Animating a clip-path, applying a blur filter, or experimenting with additional 3D transforms could all produce interesting results.
A Different Approach – GSAP Demo 2A
Another way to create a seamless looping carousel with GSAP is to use the horizontalLoop() helper function. Although GSAP helper functions aren’t officially part of the core library, they’re a handy collection of code snippets and shortcuts. They also serve as great learning resources for writing more advanced GSAP code.
This specific helper function animates elements along their x-axis and repositions them once they’re out of view to create an infinite loop. Here’s a basic implementation:
Again, we override the CSS and display the
<nav>
element. Then we callhorizontalLoop()
, which takes two parameters: an array of the carousel slides and a config object for setting various options.const loop = horizontalLoop(slides, { paused: true, // no auto-scroll paddingRight: 10, // match the 10px flex gap center: true, // snap the active slide to the center onChange: (slide, index) => { // called when the active slide changes if (activeSlide) { gsap.to(".active", { opacity: 0.3 }); activeSlide.classList.remove("active"); } slide.classList.add("active"); activeSlide = slide; gsap.to(".active", { opacity: 1, ease: "power2.inOut" }); gsap.set(".carousel-nav div", { innerText: `${index + 1}/${slides.length}` }); } });
The most notable of these options is the
onChange
callback, where we can write code that executes each time the active slide changes. In this example, we’re removing and adding the “active” class name and tweening the opacity to draw more focus to the center slide.The helper function returns a timeline with several useful added methods, including
next()
,previous()
, andtoIndex()
. We’ll use these to add navigation functionality to our previous/next buttons, as well as to the individual slides:next.addEventListener("click", () => loop.next({ duration: 1, ease: "expo" })); prev.addEventListener("click", () => loop.previous({ duration: 1, ease: "expo" })); // each slide can function as a button to activate itself slides.forEach((slide, i) => { slide.addEventListener("click", () => loop.toIndex(i, {duration: 1, ease: "expo"})) });
Finally, we set the initial carousel state by adjusting the opacity of each slide and calling
toIndex()
with no tween duration, which centers the active slide.gsap.set(".carousel-slide", { opacity: (i) => (i === 0 ? 1 : 0.3) }); loop.toIndex(0, { duration: 0 });
If you’re unfamiliar with function-based values in GSAP, this is an amazing feature—definitely check out that link to learn how they work. Here, we’re iterating through each element with the class name “carousel-slide,” returning an opacity value of 1 for the first slide and 0.3 for the rest.
The remainder of the JS is just the helper function, copied and pasted from the GSAP docs demo. In most cases, you won’t need to modify anything inside it. (We’ll look at an exception in Demo 2C.)
Add Draggable & InertiaPlugin – GSAP Demo 2B
To make the carousel move on drag, we’ll need two plugins: Draggable and the Inertia Plugin. Once those scripts are included, you can set
draggable: true
in the config object.In addition to drag behavior, this iteration includes some text animation, with logic to prevent it from running on the first load (plus hover in/out animations on the nav buttons).
onChange: (slide, index) => { // called when the active slide changes if (activeSlide) { gsap.to(".carousel h2, .carousel h5", { overwrite: true, opacity: 0, ease: "power3" }); gsap.to(".active", { opacity: 0.3 }); activeSlide.classList.remove("active"); } slide.classList.add("active"); activeSlide = slide; // intro animation for new active slide gsap.timeline({ defaults:{ ease:"power1.inOut" } }) // fade in the new active slide .to(".active", { opacity: 1, ease: "power2.inOut" }, 0) // fade out the progress text, change its value, fade it back in .to(".carousel-nav div", { duration: 0.2, opacity: 0, ease: "power1.in" }, 0) .set(".carousel-nav div", { innerText: `${index + 1}/${slides.length}` }, 0.2) .to(".carousel-nav div", { duration: 0.4, opacity: 0.5, ease: "power1.inOut" }, 0.2) // fade in the text elements and translate them vertically .to(".active h2, .active h5", { opacity: 1, ease: "power1.inOut" }, 0.3) .fromTo(".active h2, .active h5", { y:(i)=>[40,60][i] },{ duration: 1.5, y: 0, ease: "expo" }, 0.3) // skip active slide animation on first run .progress( firstRun? 1: 0 ) }
Adding Parallax – GSAP Demo 2C
To make the movement more engaging, let’s calculate each slide’s horizontal progress and use it to create a parallax effect.
Until now, we haven’t modified the helper function. However, to calculate slide progress, this version includes one change inside
horizontalLoop()
.Now, every time the carousel timeline updates,
slideImgUpdate()
is called. This function sets each image’sxPercent
based on the progress of its parent slide. Progress is 0 when the slide is offstage to the left, and 1 when it’s offstage to the right.function slideImgUpdate(){ slides.forEach( slide => { const rect = slide.getBoundingClientRect(); const prog = gsap.utils.mapRange(-rect.width, innerWidth, 0, 1, rect.x); const val = gsap.utils.clamp(0, 1, prog ); gsap.set(slide.querySelector("img"), { xPercent: gsap.utils.interpolate(0, -50, val) }); }); }
GSAP utility functions mapRange(), interpolate(), and clamp() make the progress calculation much easier. Note, in the CSS, the width of .carousel-slide img is increased to 150%, so there will be enough image for a 50% horizontal movement.
Taking It Further
There are endless ways you could build on these demos, customizing both appearance and functionality. A few ideas include:
- Modify how many slides are shown at once—a single, full-frame version could be interesting, as could several smaller slides to create a cover flow effect. In both of those examples, the progress indicator also became a fun area for experimentation.
- Additional details could be added by calling custom functions inside the helper function’s
onPress
,onRelease
, oronThrowComplete
callbacks. Here’s one more iteration on Demo 2, where the entire carousel shrinks while the pointer is held down. - The carousel could even serve as navigation for a separate animated page element, like on Nite Riot.
- If you want the carousel to respond to mouse wheel movements, GSAP’s Observer plugin offers an easy way to handle those events.
- With GSAP’s matchMedia(), you can specify different animations for various viewport widths and tailor behavior for users who prefer reduced motion.
-
Upgrading a 20 year old University Project to .NET 6 with dotnet-upgrade-assistant
I wrote a Tiny Virtual Operating System for a 300-level OS class in C# for college back in 2001 (?) and later moved it to VB.NET in 2002. This is all pre-.NET Core, and on early .NET 1.1 or 2.0 on Windows. I moved it to GitHub 5 years ago and ported it to .NET Core 2.0 at the time. At this point it was 15 years old, so it was cool to see this project running on Windows, Linux, in Docker, and on a Raspberry Pi…a machine that didn’t exist when the project was originally written.
NOTE: If the timeline is confusing, I had already been working in industry for years at this point but was still plugging away at my 4 year degree at night. It eventually took 11 years to complete my BS in Software Engineering.
This evening, as the children slept, I wanted to see if I could run the .NET Upgrade Assistant on this now 20 year old app and get it running on .NET 6.
Let’s start:
$ upgrade-assistant upgrade .\TinyOS.sln
-----------------------------------------------------------------------------------------------------------------
Microsoft .NET Upgrade Assistant v0.3.256001+3c4e05c787f588e940fe73bfa78d7eedfe0190bdWe are interested in your feedback! Please use the following link to open a survey: https://aka.ms/DotNetUASurvey
-----------------------------------------------------------------------------------------------------------------[22:58:01 INF] Loaded 5 extensions
[22:58:02 INF] Using MSBuild from C:\Program Files\dotnet\sdk\6.0.100\
[22:58:02 INF] Using Visual Studio install from C:\Program Files\Microsoft Visual Studio\2022\Preview [v17]
[22:58:06 INF] Initializing upgrade step Select an entrypoint
[22:58:07 INF] Setting entrypoint to only project in solution: C:\Users\scott\TinyOS\src\TinyOSCore\TinyOSCore.csproj
[22:58:07 INF] Recommending executable TFM net6.0 because the project builds to an executable
[22:58:07 INF] Initializing upgrade step Select project to upgrade
[22:58:07 INF] Recommending executable TFM net6.0 because the project builds to an executable
[22:58:07 INF] Recommending executable TFM net6.0 because the project builds to an executable
[22:58:07 INF] Initializing upgrade step Back up projectSee how the process is interactive at the command line, with color prompts and a series of dynamic multiple-choice questions?
Interestingly, it builds on the first try, no errors.
When I manually look at the .csproj I can see some weird version numbers, likely from some not-quite-baked version of .NET Core 2 I used many years ago. My spidey sense says this is wrong, and I’m assuming the upgrade assistant didn’t understand it.
<!-- <PackageReference Include="ILLink.Tasks" Version="0.1.4-preview-906439" /> -->
<PackageReference Include="Microsoft.Extensions.Configuration" Version="2.0.0-preview2-final" />
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="2.0.0-preview2-final" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="2.0.0-preview2-final" />
<PackageReference Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="2.0.0-preview2-final" />I also note a commented-out reference to ILLink.Tasks which was a preview feature in Mono’s Linker to reduce the final size of apps and tree-trim them. Some of that functionality is built into .NET 6 now so I’ll use that during the build and packaging process later. The reference is not needed today.
I’m gonna blindly upgrade them to .NET 6 and see what happens. I could do this by just changing the numbers and seeing if it restores and builds, but I can also try dotnet outdated which remains a lovely tool in the upgrader’s toolkit.
This “outdated” tool is nice as it talks to NuGet and confirms that there are newer versions of certain packages.
In my tests – which were just batch files at this early time – I was calling my dotnet app like this:
dotnet netcoreapp2.0/TinyOSCore.dll 512 scott13.txt
This will change to the modern form with just
TinyOSCore.exe 512 scott13.txt
with an exe and args and no ceremony.Publishing and trimming my TinyOS turns into just a 15 meg EXE. Nice considering that the .NET I need is in there with no separate install. I could turn this little synthetic OS into a microservice if I wanted to be totally extra.
dotnet publish -r win-x64 --self-contained -p:PublishSingleFile=true -p:SuppressTrimAnalysisWarnings=true
If I add
-p:EnableCompressionInSingleFile=true
Then it’s even smaller. No code changes. Run all my tests, looks good. My project from university from .NET 1.1 is now .NET 6.0, cross platform, self-contained in 11 megs in a single EXE. Sweet.
Sponsor: At Rocket Mortgage® the work you do around here will be 100% impactful but won’t take all your free time, giving you the perfect work-life balance. Or as we call it, tech/life balance! Learn more.
About Scott
Scott Hanselman is a former professor, former Chief Architect in finance, now speaker, consultant, father, diabetic, and Microsoft employee. He is a failed stand-up comic, a cornrower, and a book author.
-
Build a Python Site Connectivity Checker App with PyQt (Step-by-Step)
Build a Python Site Connectivity Checker App with PyQt (Step-by-Step)
Source link -
Build a Python Network Speed Test App with PyQt (Step-by-Step)
Build a Python Network Speed Test App with PyQt (Step-by-Step)
Source link -
Build a Python Secure File Eraser App with PyQt (Step-by-Step)
Build a Python Secure File Eraser App with PyQt (Step-by-Step)
Source link